深入反应 useEffect / useEffect 的使用?

Joh*_*_ny 17 javascript reactjs react-redux react-hooks

我试图深入了解useEffect钩子。

我想知道何时使用哪种方法以及为什么?

1.useEffect with no second paraments
 useEffect(()=>{})

2.useEffect with second paraments as []
  useEffect(()=>{},[])

3.useEffect with some arguments passed in the second parameter
 useEffect(()=>{},[arg])
Run Code Online (Sandbox Code Playgroud)

Den*_*ash 32

useEffect(callback);

// Example
useEffect(() => {
  console.log("executed after render phase");

  return () => {
    console.log("cleanup function after render");
  };
});
Run Code Online (Sandbox Code Playgroud)
  • Runs on every component render.
  • Typically used for debugging, analogous to function's body execution on every render:
const Component = () => {
  callback()
  return <></>;
};
Run Code Online (Sandbox Code Playgroud)

Note: There is still a difference, in execution time (see the next note). Check this sandbox logs.


useEffect(callback,[]);

// Example
useEffect(() => {
  const fetchUsers = async () => {
    const users = await fetch();
    setUsers(users);
  };

  fetchUsers();
  console.log("called on component's mount");

  return () => {
    console.log("called on component's unmount");
  };
}, []);
Run Code Online (Sandbox Code Playgroud)
  • Usually used for initializing components state by data fetching etc.
  • Runs once on a component mount.
  • The cleanup function will run on component's unmount.

Gotchas:

Remember, there is a first render and then a mount.

Roughly saying, most of bugs regarding useEffect is not knowing how closures works and not paying attention to linting warnings.

Make sure the array includes all values from the component scope that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders - note in React docs.


useEffect(callback,[arg]);

// Example
useEffect(() => {
  console.log({ users });

  return () => {
    console.log("user value is changing");
  };
}, [users]);
Run Code Online (Sandbox Code Playgroud)
  • Runs on change of arg value.
  • Usually used to run events on props/state change.
  • Multiple dependencies can be provided: [arg1,arg2,arg3...]
  • The cleanup function runs on arg value change.

Gotchas:

i.e compares the value of arg from the previous render and the current one, prevArg === arg ? doNothing() : callback().

  • Because in Javascript {} === {} || [] === [] is a falsy statement, if arg (users in our example) is an object, the callback will run on every render.

Additional Good to Know Points

const timeoutIdRef = useRef();

useEffect(() => {
  const timeoutId = timeoutIdRef.current;
  return () => {
  /*
    Using timeoutIdRef.current directly here is not safe
    since you can't guarantee the ref to exists in this point
    (especially when the component unmounts)
  */
    // Should get a lint warning here
    clearTimeout(timeoutIdRef.current); // BAD

    // Closure on timeoutId value
    clearTimeout(timeoutId); // GOOD
  };
}, [arg]);
Run Code Online (Sandbox Code Playgroud)
const isMounted = useRef(false);

useEffect(() => {
  if (isMounted.current) {
    // first mount
  } else {
    isMounted.current = true;
  }
}, [arg]);
Run Code Online (Sandbox Code Playgroud)

Keep reading:

  • 请注意,缺少依赖项可能会导致[过时的闭包](https://dmitripavlutin.com/react-hooks-stale-closures/)。代码如下: `const [s, setS] = useState(0); useEffect(() =&gt; setInterval(() =&gt; console.log(s), 1), []);` 只会记录 0,因为回调在 s 上关闭,即使该函数在每次渲染时都重新创建只会在挂载时调用,并且 s 为 0 时。看到多次弹出与此错误有关的问题,并且状态的异步设置被归咎于功能组件,其根本原因是过时的闭包。 (3认同)