【问题标题】:Understanding React hooks useEffect function.When is the callback function(i.e unsubscribe) in useffect gets called?了解 React 钩子 useEffect 函数。useEffect 中的回调函数(即取消订阅)何时被调用?
【发布时间】:2020-03-30 00:29:25
【问题描述】:

我一直在试图了解取消订阅(useEffect 中的回调)何时被准确调用。

这是 codepen 链接:https://codepen.io/deen_john/pen/eYmNdMy 代码:

const { useState, useEffect } = React  ;
 
function App() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
    console.log("use effect called ");
    
    return () => {
      console.log("unsubscribe ")
    }
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

问题: 在我的示例中,useEffect 挂钩(即取消订阅)中的回调函数在我每次单击按钮时被调用(即每次我更新按钮状态时)。 但是,根据 React 文档, useEffect 中的回调的工作方式类似于 componentWillUnmount 生命周期,因此在我的示例中,只有在卸载 App 组件时才应该调用它。 我只是在这里更新按钮状态,而不是每次点击都卸载 App 组件。

【问题讨论】:

标签: reactjs react-hooks


【解决方案1】:

每次调用useEffect之前以及组件卸载时都会调用useEffect的取消订阅事件

由于在您的情况下,您没有将任何依赖数组传递给 useEffect,因此您的 useEffect 将在组件的每次渲染上运行,因此在触发下一次 useEffect 之前,取消订阅将在每次重新渲染时运行

编写上述代码的更好方法是将依赖数组中的计数传递给 useEffect

const { useState, useEffect } = React;
 
function App() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
    console.log("use effect called ");
    
    return () => {
      console.log("unsubscribe ")
    }
  }, [count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

【讨论】:

    【解决方案2】:

    React 将始终尝试保持您的效果更新。因此,每当它们运行时,默认情况下会尝试清理最后一个效果以应用新效果。

    • 每次渲染后运行的效果

    因此,如果您的效果在每次渲染后运行,React 将在每次渲染后进行清理,然后应用新效果。

    • 基于某些依赖项运行的效果

    如果你的效果依赖于一些变量(包含在依赖数组[a,b, etc]中来决定它是否应该再次运行,同样会发生。如果它再次运行,React会在运行效果之前运行清理功能。

    • 仅在第一次渲染后运行的效果

    如果您的效果仅在第一次渲染后运行(使用空的依赖数组 []),那么 React 只会在组件卸载后进行清理。

    示例:

    下面的 sn-p 显示了这种行为。

    function App() {
      console.log('Rendering App...');
      const [show2,setShow2] = React.useState(true);
      const [stateBool,setStateBool] = React.useState(true);
      return(
        <React.Fragment>
          <button onClick={()=>{
            console.clear();
            setStateBool((prevState)=>!prevState)}
          }>
            Update App
          </button>
          <button onClick={()=>{
            console.clear()
            setShow2((prevState)=>!prevState)}
          }>
            Toggle Component2
          </button>
          <Component1/>
          {show2 && <Component2/>}
        </React.Fragment>
      );
    }
    
    function Component1() {
      console.log('Rendering Component1...');
      React.useEffect(()=>{
        console.log('useEffect Component1 after every render...');
        return () => console.log('Cleaning up useEffect Component1...');
      });
      return(
        <div>Component1</div>
      );
    }
    
    function Component2() {
      console.log('Rendering Component2...');
      React.useEffect(()=>{
        console.log('useEffect Component2 only after 1st render...');
        return () => console.log('Cleaning up useEffect Component2...');
      },[]);
      return(
        <div>Component2</div>
      );
    }
    
    ReactDOM.render(<App/>, document.getElementById('root'));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
    <div id="root"/>

    【讨论】:

      猜你喜欢
      • 2021-11-12
      • 2021-12-09
      • 1970-01-01
      • 2019-04-12
      • 2020-05-11
      • 1970-01-01
      • 1970-01-01
      • 2021-10-16
      • 2020-08-08
      相关资源
      最近更新 更多