为什么每次渲染都会调用来自 `useEffect` 的清理函数? [英] Why is the cleanup function from `useEffect` called on every render?

查看:72
本文介绍了为什么每次渲染都会调用来自 `useEffect` 的清理函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在学习 React,我读到从 useEffect 返回的函数是为了进行清理,而 React 在组件卸载时执行清理.

所以我对它进行了一些试验,但在下面的示例中发现,每次组件重新渲染时都会调用该函数,而不是仅在从 DOM 卸载时调用该函数,即 console.log("unmount"); 每次组件重新渲染时.

这是为什么?

function something({ setShow }) {const [array, setArray] = useState([]);const myRef = useRef(null);useEffect(() => {const id = setInterval(() => {setArray(array.concat("你好"));}, 3000);myRef.current = id;返回 () =>{console.log("卸载");clearInterval(myRef.current);};}, [大批]);const 卸载 = () =>{设置显示(假);};返回 (<div>{array.map((item, index) => {返回 (<p key={index}>{数组(索引 + 1).fill(项目).加入("")}</p>);})}<button onClick={() =>unmount()}>close</button>

);}功能应用(){const [show, setShow] = useState(true);回归表演?<Something setShow={setShow}/>: 空值;}

实例:https://codesandbox.io/s/vigilant-leavitt-z1jd2

React 在组件卸载时执行清理.

我不知道你在哪里读到的,但这个说法是不正确的.React 在依赖项时执行清理钩子改变,效果钩子需要用新值再次运行.此行为是有意保持视图对更改数据的反应性.离开官方示例,假设一个应用程序从朋友的个人资料中订阅状态更新.作为您的好朋友,您决定与他们成为朋友并与其他人成为朋友.现在该应用程序需要取消订阅前一个朋友的状态更新并听取新朋友的更新.通过 useEffect 的工作方式,这是自然而容易实现的.

 useEffect(() => {chatAPI.subscribe(props.friend.id);返回 () =>chatAPI.unsubscribe(props.friend.id);}, [ props.friend.id ])

通过在依赖列表中包含好友 id,我们可以表明钩子只需要在好友 id 发生变化时运行.

在您的示例中,您已在依赖项列表中指定了 array 并且您正在以设定的间隔更改数组.每次更改数组时,钩子都会重新运行.

您只需从依赖项列表中删除数组并使用 setState 钩子的回调版本即可实现正确的功能.回调版本总是对上一版本的状态进行操作,所以不需要每次数组变化都刷新钩子.

 useEffect(() => {const id = setInterval(() => setArray(array => [ ...array, "hello" ]), 3000);返回 () =>{console.log("卸载");clearInterval(id);};}, []);

一些额外的反馈是直接在 clearInterval 中使用 id,因为该值在您创建清理函数时关闭(捕获).无需将其保存到 ref.

I've been learning React and I read that the function returned from useEffect is meant to do cleanup and React performs the cleanup when the component unmounts.

So I experimented with it a bit but found in the following example that the function was called every time the component re-renders as opposed to only the time it got unmounted from the DOM, i.e. it console.log("unmount"); every time the component re-renders.

Why is that?

function Something({ setShow }) {
  const [array, setArray] = useState([]);
  const myRef = useRef(null);

  useEffect(() => {
    const id = setInterval(() => {
      setArray(array.concat("hello"));
    }, 3000);
    myRef.current = id;
    return () => {
      console.log("unmount");
      clearInterval(myRef.current);
    };
  }, [array]);

  const unmount = () => {
    setShow(false);
  };

  return (
    <div>
      {array.map((item, index) => {
        return (
          <p key={index}>
            {Array(index + 1)
              .fill(item)
              .join("")}
          </p>
        );
      })}
      <button onClick={() => unmount()}>close</button>
    </div>
  );
}

function App() {
  const [show, setShow] = useState(true);

  return show ? <Something setShow={setShow} /> : null;
}

Live example: https://codesandbox.io/s/vigilant-leavitt-z1jd2

解决方案

React performs the cleanup when the component unmounts.

I'm not sure where you read this but this statement is incorrect. React performs the cleanup when the dependencies to that hook changes and the effect hook needs to run again with new values. This behaviour is intentional to maintain the reactivity of the view to changing data. Going off the official example, let's say an app subscribes to status updates from a friends' profile. Being the great friend you are, you are decide to unfriend them and befriend someone else. Now the app needs to unsubscribe from the previous friend's status updates and listen to updates from your new friend. This is natural and easy to achieve with the way useEffect works.

 useEffect(() => { 
    chatAPI.subscribe(props.friend.id);

    return () => chatAPI.unsubscribe(props.friend.id);
  }, [ props.friend.id ])

By including the friend id in the dependency list, we can indicate that the hook needs to run only when the friend id changes.

In your example you have specified the array in the dependency list and you are changing the array at a set interval. Every time you change the array, the hook reruns.

You can achieve the correct functionality simply by removing the array from the dependency list and using the callback version of the setState hook. The callback version always operates on the previous version of the state, so there is no need to refresh the hook every time the array changes.

  useEffect(() => {
    const id = setInterval(() => setArray(array => [ ...array, "hello" ]), 3000);

    return () => {
      console.log("unmount");
      clearInterval(id);
    };
  }, []);

Some additional feedback would be to use the id directly in clearInterval as the value is closed upon (captured) when you create the cleanup function. There is no need to save it to a ref.

这篇关于为什么每次渲染都会调用来自 `useEffect` 的清理函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆