当我调用useState挂钩时重新渲染React组件

时间:2019-11-14 07:52:15

标签: reactjs typescript react-hooks tensorflow.js

当我在useState()内调用image.onload()钩子时,重新渲染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;

关于如何避免疯狂重新渲染的任何想法?

1 个答案:

答案 0 :(得分:1)

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

您需要重新考虑算法,并将其移至更好的位置。一个好的解决方案是只准备图片onDrop event,这样就只能完成一次。

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

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