我一直在试图了解何时完全调用取消订阅(useEffect中的回调(。
这是代码笔链接: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 中的回调的工作方式类似于 组件将卸载生命周期 ,所以在我的示例中,只有在卸载 App 组件时才应该调用它。 我只是在这里更新按钮状态,而不是在每次单击时卸载应用程序组件。
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>
React 将始终尝试保持效果更新。因此,每当它们运行时,默认情况下,它将尝试清理最后一个效果以应用新效果。
- 每次渲染后运行的效果
因此,如果你的效果在每次渲染后运行,React 将在每次渲染后进行清理,然后应用新效果。
- 基于某些依赖项运行的效果
如果效果依赖于某些变量(包含在依赖项数组中([a,b, etc]
来决定是否应再次运行,则也会发生同样的情况。如果它会再次运行,React 将在运行效果之前运行清理函数。
- 仅在第一次渲染后运行的效果
如果你的效果只会在第一次渲染后运行([]
有一个空的依赖数组(,那么 React 只会在组件卸载后进行清理。
例:
下面的代码段显示了这种行为。
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"/>