为什么钩子中的函数依赖性不会导致无限渲染
Why doesn't a function dependency in hooks cause infinite renders
以下是片段 link to codesandbox:
// 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 再次渲染组件)。
所以上面的代码片段 运行 不应该在无限循环中吗?
示例和基本理解来自 A Complete Guide to useEffect
当使用 useState
时,反应足够聪明,可以跳过 re-rendering 如果状态值
尽管调用了 setState
函数,但实际上并没有改变。 (这记录在 https://reactjs.org/docs/hooks-reference.html#bailing-out-of-a-state-update)
您提供的示例代码与链接的代码笔略有不同,实际上只会产生一条 "set" 控制台日志消息。
running effect: 15
running effect: 21
事件的顺序是:
- 初始渲染,触发两种效果并更新
reactResult
和 reduxResult
状态。这会使 re-render. 排队
- 组件re-renders。在您包含的示例中,您使用的
useCallback
没有任何依赖关系,这将 return 先前的值,因此效果不会 运行.
另一方面,在您的代码笔中,您在每次执行时重新定义回调,而不是 useCallback
,在这种情况下,您将获得两条 "sets" 控制台消息:
- 初始渲染,触发两种效果并更新
reactResult
和 reduxResult
状态。这会使 re-render. 排队
- 组件re-renders。
getFetchUrl
是局部函数,因此 不 等于 getFetchUrl
来自之前的 运行。因此,效果将 re-run。但是 setReactResult
和 setReduxResult
都使用与之前相同的值调用,因此 re-render 将 而不是 被触发。
您的效果仅在 getFetchUrl
更改时触发...并且由于它是一个记忆回调(不会更改)效果仅 运行 一次。
以下是片段 link to codesandbox:
// 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 再次渲染组件)。
所以上面的代码片段 运行 不应该在无限循环中吗?
示例和基本理解来自 A Complete Guide to useEffect
当使用 useState
时,反应足够聪明,可以跳过 re-rendering 如果状态值
尽管调用了 setState
函数,但实际上并没有改变。 (这记录在 https://reactjs.org/docs/hooks-reference.html#bailing-out-of-a-state-update)
您提供的示例代码与链接的代码笔略有不同,实际上只会产生一条 "set" 控制台日志消息。
running effect: 15
running effect: 21
事件的顺序是:
- 初始渲染,触发两种效果并更新
reactResult
和reduxResult
状态。这会使 re-render. 排队
- 组件re-renders。在您包含的示例中,您使用的
useCallback
没有任何依赖关系,这将 return 先前的值,因此效果不会 运行.
另一方面,在您的代码笔中,您在每次执行时重新定义回调,而不是 useCallback
,在这种情况下,您将获得两条 "sets" 控制台消息:
- 初始渲染,触发两种效果并更新
reactResult
和reduxResult
状态。这会使 re-render. 排队
- 组件re-renders。
getFetchUrl
是局部函数,因此 不 等于getFetchUrl
来自之前的 运行。因此,效果将 re-run。但是setReactResult
和setReduxResult
都使用与之前相同的值调用,因此 re-render 将 而不是 被触发。
您的效果仅在 getFetchUrl
更改时触发...并且由于它是一个记忆回调(不会更改)效果仅 运行 一次。