调用 useState 钩子时重新渲染 React 组件

Re-rendering React component when I call useState hook

当我在 image.onload() 中调用 useState() 挂钩时,我在重新渲染 React 组件时遇到问题。我希望组件在我调用 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;

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

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

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

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

然后可能会在状态中存储一个 图像数组,应该显示并准备好。