useEffect 中可以有条件地进行不同的清理吗?

tsu*_*uki 6 reactjs react-hooks use-effect

所以,我有useEffect这样的:

useEffect(()=>{
    if(foo) {
        // do something
        return () => { // cleanup function }
    }
}, [foo])
Run Code Online (Sandbox Code Playgroud)

在这里,cleanup即使if块被执行,该函数也永远不会被调用。但如果我修改一下效果为:

useEffect(()=>{
    if(foo) {
        // do something
    }
    return () => { // cleanup function }
}, [foo])
Run Code Online (Sandbox Code Playgroud)

有用。那么,清理工作是否仅return在最后一条语句useEffect或我缺少某些内容时才进行?

Ori*_*ori 8

每次useEffect调用更新函数时都会重新创建清理函数。当依赖项发生变化时(如果没有依赖项,则在每次渲染时)或在卸载组件之前,将调用当前的清理函数。调用 cleanup 后,将调用 updater,并返回一个新的 cleanup 函数。

每当更新程序函数被调用时,您都可以返回不同的函数,或者根本不返回任何函数。

例如,多次单击Inccounter按钮,您可以在控制台中看到清理函数仅适用于偶数,因为它是有条件返回的。

const { useState, useEffect } = React;

const Demo = () => {
  const [counter, setCount] = useState(0);
  
  useEffect(() => {
    console.log(`Effect ${counter}`);
    
    if(counter % 2 === 0) {
      return () => console.log(`Cleanup ${counter}`);
    }
  }, [counter]);
  
  return (
    <div>
      <p>Counter: {counter}</p>
      
      <button onClick={() => setCount(counter + 1)}>Inc</button>
    </div>
  );
};

ReactDOM.render(
  <Demo />,
  root
);
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { top: 0; left: 50% !important; max-height: unset !important; }
Run Code Online (Sandbox Code Playgroud)
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)

有时,将清理函数嵌套在条件中可能会令人困惑。另一种选择是始终返回清理函数,并将逻辑放入其中:

useEffect(()=>{
    if(foo) {
        // do something
    }
    
    return () => { 
      if(foo) {
        // cleanup something
      }
    }
}, [foo])
Run Code Online (Sandbox Code Playgroud)

您可以在这个示例中看到,结果是相同的:

useEffect(()=>{
    if(foo) {
        // do something
    }
    
    return () => { 
      if(foo) {
        // cleanup something
      }
    }
}, [foo])
Run Code Online (Sandbox Code Playgroud)
const { useState, useEffect } = React;

const Demo = () => {
  const [counter, setCount] = useState(0);
  
  useEffect(() => {
    console.log(`Effect ${counter}`);

    return () => {
      if(counter % 2 === 0) {
        console.log(`Cleanup ${counter}`);
      }    
    };
  }, [counter]);
  
  return (
    <div>
      <p>Counter: {counter}</p>
      
      <button onClick={() => setCount(counter + 1)}>Inc</button>
    </div>
  );
};

ReactDOM.render(
  <Demo />,
  root
);
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { top: 0; left: 50% !important; max-height: unset !important; }
Run Code Online (Sandbox Code Playgroud)