以下是代码沙盒的代码片段链接:
// function getFetchUrl(query) {
// return "https://hn.algolia.com/api/v1/search?query=" + query;
// }
function App() {
const [reactResult, setReactResult] = useState(null);
const [reduxResult, setReduxResult] = useState(null);
function SearchResults() {
// 🔴 Re-triggers all effects on every render
// const getFetchUrl = useCallback((query) => {
// return "https://hn.algolia.com/api/v1/search?query=" + query;
// }, []);
function getFetchUrl(query) {
return "https://hn.algolia.com/api/v1/search?query=" + query;
}
useEffect(() => {
console.log("running effect: 15");
setReactResult(getFetchUrl("react"));
// ... Fetch data and do something ...
// }, [getFetchUrl]); // 🚧 Deps are correct but they change too often
}, [getFetchUrl]);
useEffect(() => {
console.log("running effect: 21");
setReduxResult(getFetchUrl("redux"));
// ... Fetch data and do something ...
// }, [getFetchUrl]); // 🚧 Deps are correct but they change too often
}, [getFetchUrl]);
// ...
}
SearchResults();
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>{reactResult}</h2>
<h2>{reduxResult}</h2>
</div>
);
}
控制台中的输出为
running effect: 15
running effect: 21
running effect: 15
running effect: 21
我已经检查了这个答案,我得到的是重新定义了函数,这导致useEffect
再次运行(第二次)。但我想澄清一个疑问:
当useEffect
第二次运行时,它会调用stateSetter
函数(要求 React 再次渲染组件)。
那么上面的代码片段不应该在无限循环中运行吗?
示例和基本理解摘自使用效果完整指南
当使用useState
反应足够聪明,可以跳过重新渲染,如果状态值 尽管调用了setState
函数,但实际上并没有改变。(这记录在 https://reactjs.org/docs/hooks-reference.html#bailing-out-of-a-state-update)
您包含的示例代码与链接的代码笔略有不同,实际上只会生成单个"集"控制台日志消息。
running effect: 15
running effect: 21
事件的顺序是:
- 初始渲染会触发效果并更新
reactResult
和reduxResult
状态。这会将重新渲染排队。 - 组件将重新呈现。在您包含的示例中,您使用的是没有依赖项的
useCallback
,它将返回以前的值,因此效果将不会运行。
另一方面,在代码笔中,不是useCallback
重新定义每次执行时的回调,在这种情况下,您将获得两"组"控制台消息:
- 初始渲染会触发效果并更新
reactResult
和reduxResult
状态。这会将重新渲染排队。 - 组件将重新呈现。
getFetchUrl
是本地函数,因此不等于上一次运行的getFetchUrl
。因此,效果将重新运行。但是,setReactResult
和setReduxResult
都使用与以前相同的值调用,因此不会触发重新渲染。
您的效果仅在getFetchUrl
更改时触发...由于它是一个记忆回调(不会更改),因此效果只运行一次。