首页 > 解决方案 > 当我调用 useState 挂钩时重新渲染 React 组件

问题描述

useState()当我调用hook inside时,我遇到了重新渲染 React 组件的问题image.onload()。我希望组件在我调用后重新渲染一次setClassificationResult,但由于某种原因,它一直在重新渲染,就像我有一些无限循环一样。这是我的代码:

const ImageClassification = React.memo(function() {
  const [isModelLoaded, setModelLoaded] = useState(false);
  const [uploadedFile, setUploadedFile] = useState();
  const [classifier, setClassifier] = useState();
  const [classificationResult, setClassificationResult] = useState();

  useEffect(() => {
    async function modelReady() {
      setClassifier(
        await MobileNet.load().then(model => {
          setModelLoaded(true);
          return model;
        })
      );
    }

    modelReady();
  }, []);

  function onDrop(acceptedFiles: File[]) {
    setUploadedFile(acceptedFiles);
  }

  function prepareImage(inputFile: File) {
    const image = new Image();
    let fr = new FileReader();

    fr.onload = function() {
      if (fr !== null && typeof fr.result == "string") {
        image.src = fr.result;
      }
    };
    fr.readAsDataURL(inputFile);

    image.onload = async function() {
      const tensor: Tensor = tf.browser.fromPixels(image);
      classifier.classify(tensor).then((result: any) => {
        // Crazy re-rendering happens when I call this hook.
        setClassificationResult(result);
        console.log(result);
      });
      console.log("Tensor" + tensor);
    };
  }

  const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });

  return (
    <React.Fragment>
      {!isModelLoaded ? (
        <CircularProgress />
      ) : (
        <div {...getRootProps()}>
          <input {...getInputProps()} />
          {isDragActive ? (
            <p>Drop the files here.. </p>
          ) : (
            <p>Drag 'n' drop some files here, or click to select files</p>
          )}
          {uploadedFile &&
            uploadedFile.map((item: File) => {
              prepareImage(item);
              return classificationResult
                ? classificationResult.map((result: any) => {
                    return (
                      <ClassificationResult
                        className={result.className}
                        probability={result.probability}
                      />
                    );
                  })
                : null;
            })}
        </div>
      )}
    </React.Fragment>
  );
});

export default ImageClassification;

知道如何避免这种疯狂的重新渲染吗?

标签: reactjstypescriptreact-hookstensorflow.js

解决方案


您的组件存在生命周期问题,因为它prepareImage(item)从您的returnhtml 值调用。这意味着您将在每次渲染时调用此函数,这就是为什么它会创建一些无限循环的疯狂重新渲染。

您需要重新考虑您的算法并将其移动到更好的位置。一个好的解决方案是只准备图像onDrop event,所以它只完成一次。

function onDrop(acceptedFiles: File[]) {
    setUploadedFile(acceptedFiles);
    acceptedFiles.forEach(file => {
        prepareImage(file);
    });
}

然后可以在状态中存储一个应该显示和准备的 Image 数组。


推荐阅读