尝试在 useEffect 钩子中使用清理函数来清理 img.onload

owe*_*upt 5 javascript reactjs react-hooks

我最近构建了一个 React 组件(称为 ItemIndexItem),它在我的应用程序上显示图像。例如,我有一个 Search 组件,它将显示过滤项目的索引。显示的每个 Item 都是一个 ItemIndexItem 组件。单击 ItemIndexItem 会将您转到使用相同 ItemIndexItem 的 ItemShow 页面。

搜索.jsx

render() {
  return (
    <ul>
      <li key={item.id}>
        <div>
          <Link to={`/items/${item.id}`}>
            <ItemIndexItem src={item.photos[0].photoUrl} />
            <p>${item.price}</p>
          </Link>
        </div>
      </li>
      ...more li's
    </ul>
  )
}
Run Code Online (Sandbox Code Playgroud)

ItemIndexItem.jsx

render() {
  return (
    <ul>
      <li key={item.id}>
        <div>
          <Link to={`/items/${item.id}`}>
            <ItemIndexItem src={item.photos[0].photoUrl} />
            <p>${item.price}</p>
          </Link>
        </div>
      </li>
      ...more li's
    </ul>
  )
}
Run Code Online (Sandbox Code Playgroud)

该组件完全按预期工作,除了在控制台中引发内存泄漏错误:

无法对卸载的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要修复,请取消 useEffect 清理函数中的所有订阅和异步任务。

在 ItemIndexItem(由 ItemShow 创建)

在 div(由 ItemShow 创建)

作为参考,这是 ItemShow 中我渲染 ItemIndexItem 的代码:

ItemShow.jsx

return (
 ...
   <div>
     <ul>
       {this.props.item.photos.map(photo => {
         return (
           <li key={photo.photoUrl}>
             <div>
               <ItemIndexItem type='show' src={photo.photoUrl} />
             </div>
           </li>
         );
       })}
     </ul>
   </div>
 ...
Run Code Online (Sandbox Code Playgroud)

我尝试使用 useEffect 返回函数设置img为 null:

return () => img = null;
Run Code Online (Sandbox Code Playgroud)

然而,这没有任何作用。由于我没有创建订阅,因此没有要删除的订阅。所以我认为问题在于.onload.

Als*_*rda 5

您正在设置不再安装的组件的状态。您可以使用useRef钩子来确定您的组件是否仍然安装,例如:

function useIsMounted() {
  const isMounted = React.useRef(true);

  React.useEffect(() => {
    return () => {
      isMounted.current = false;
    };
  }, []);

  return isMounted;
}
Run Code Online (Sandbox Code Playgroud)

而在你的ItemIndexItem...

export default function ItemIndexItem(props) {
  const isMounted = useIsMounted();
  const [imageIsReady, setImageIsReady] = useState(false);

  ...
  img.onload = () => {
    if (isMounted.current) {
      setImageIsReady(true);
    }
  ...
}
Run Code Online (Sandbox Code Playgroud)

useRef的 React 文档中所述

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传递的参数 (initialValue)。返回的对象将在组件的整个生命周期内持续存在。

这意味着您可以使用它来创建对 HTML 元素的引用,但您也可以在该引用中放置其他变量,例如布尔值。在我的“useIsMounted”钩子的情况下,它在初始化时将其设置为已安装,并在卸载时将其设置为未安装。