React useEffect 依赖项或 useCallback 似乎会导致重新渲染

React useEffect dependency or useCallback seem to cause re-renders

我有一个 useEffect 函数,可以拍摄图像、调整图像大小以供显示并准备好上传。虽然这有效,但我收到 React 警告以在 useEffect 依赖项列表中列出该函数。但是,当我这样做时,即使将函数移至 useCallback,它也会导致连续重新呈现。代码如下...

import React, { useEffect, useRef, useCallback } from 'react'

const Canvas = (props) => {

   const { width = 180, height = 220, img, onChange = null, setFile = null} = props

   const canvas = useRef(null)

   const setBlobFile = useCallback(
       (blob) => {
           setFile(blob)
       },
       [setFile]
   )

   useEffect(() => {

       if (img) {
           console.log("Canvas useEffect ...", img)
           var image = new Image()

           image.src = img
           image.onload = () => {

               canvas.current.getContext("2d")
               canvas.current.getContext("2d").clearRect(0, 0, width, height);
               canvas.current.getContext("2d").drawImage(image, 0, 0, image.width, image.height, 0, 0, width, height)
               canvas.current.toBlob((blob) => {
                   setBlobFile(blob)
               })

           }

       }
      
   }, [img, width, height, setBlobFile])

   const onFileSelect = (e) => {

       const objectURL = URL.createObjectURL(e.target.files[0])
       onChange(objectURL)

   }

   return (

       <div onClick={onClick}>
           <label htmlFor='upload'>
               <canvas ref={canvas} width={width} height={height} />
               <input type='file' id='upload' onChange={(e) => onFileSelect(e)} style={{ display: 'none' }} />
           </label>
       </div>

   )

}

export { Canvas as default }

父组件提供setFile例程来设置上传文件

...

const [selectedFile, setSelectedFile] = useState(new File([""], ""))
const setFile = (aBlob) => {

        var img = new Image()
        img = aBlob
        setSelectedFile(new File([aBlob], "image.png", {
            type: 'image/png',
          }))
        
    }
...

知道为什么我会看到这种行为吗?我认为 useCallback() 方法应该停止重新渲染,但它似乎创建了它们。我怎样才能避免警告并停止重新渲染行为?感谢您的观看。

你必须在创建函数的地方使用useCallback,而不是在使用它的地方。

   const setBlobFile = useCallback(
       (blob) => {
           setFile(blob)
       },
       [setFile]
   )

in CanvassetFile 更改时无法防止重新渲染,因为 setBlobFilesetFile 更改时仍会更改,因为 setFile 作为依赖。


相反,您需要在首先创建 setFile 的父组件中使用 useCallback

const [selectedFile, setSelectedFile] = useState(new File([""], ""))
const setFile = useCallback((aBlob) => {
    var img = new Image()
    img = aBlob
    setSelectedFile(new File([aBlob], "image.png", {
        type: 'image/png',
    }))
}, [setSelectedFile]);

这将按预期工作,因为 useState 保证 setter 函数 (setSelectedFile) 永远不会在渲染之间改变,因此 setFile 永远不会在渲染之间改变.