![](/img/trans.png)
[英]Using setInterval and updating state inside useEffect how to prevent other component to re-render
[英]How should I trigger a re-render when the state is updated inside useEffect()
我试图弄清楚我应该如何使用 React 钩子来构造我的代码,这样:
loading
state 在useEffect()
运行以获取要显示的数据时设置为true
useEffect()
结束时,我将loading
设置为false
loading
state作为prop传递来改变各种组件我的代码的简化 model 如下:
const App = () => {
const [state, stateDispatch] = useReducer(stateReducer,{loading: false})
useEffect(() => {
stateDispatch({type:'loadingResults'});
loadResults();
stateDispatch({type:'finishedLoadingResults'});
});
return (
<ExampleComponent loading={state.loading} />
);
}
const stateReducer = (prevState,action) => {
switch(action.type) {
case 'loadingResults':
return {...prevState, loading:true};
case 'finishedLoadingResults':
return {...prevState, loading: false};
default:
throw new Error();
}
}
示例“子”组件如下:
const SampleComponent = (props) => {
const [buttonText, setButtonText] = useState('Load More');
useEffect(() => {
if (props.loading) {
setButtonText('Loading...')
} else {
setButtonText('Load More');
}
},[props.loading]);
return (
<div onClick={(event) => props.getBooks(event,false)} className="moreResults">{buttonText}</div>
)
}
使用当前的设置,我永远不会看到重新渲染子组件 - 即使看起来if (props.loading)
确实被正确评估了。 例如,如果我在 if check 和 else check 中放置一个console.log
,我会在loading
state 切换时看到两者都打印出来。
或者,如果我在App
组件中定义state.loading
对useEffect()
function 有依赖关系,我会得到无限的重新渲染,因为我正在切换loading
Z9ED39E2EA931586B6E985Ef 内部useEffect()
。
知道我在这里缺少什么吗?
您的子组件中不需要额外useEffect
或useState
。 父项中的道具正在发生变化,因此足以重新渲染您的组件。
const SampleComponent = (props) => (
<div onClick={(event) => props.getBooks(event,false)}
className="moreResults">{props.loading ? 'Loading...' : 'Load More'}
</div>
)
组件仅在其任何 prop 或 state 更改时才决定重新渲染。
Javascript 由于其单线程特性而同步运行所有内容。 因此,react diffing function 以识别组件是否需要重新渲染将以同步顺序发生,即在这种情况下 useEffect 钩子执行完成之后。
useEffect(() => {
stateDispatch({type:'loadingResults'});
// -> state update happened
// -> but re-render won't happen, since useEffect metho execution is not complete yet
loadResults();
stateDispatch({type:'finishedLoadingResults'});
});
但是到那时你已经完成加载并重置 state。 由于没有变化,组件不会重新渲染。
此外,您不能在钩子之外设置 state,因为任何 setState 触发器都会触发重新渲染,这将再次设置 state,这将创建一个无限循环。
解决方案:调用 setState 以在 useEffect 块之外的 if 条件内加载。类似这样的东西。
const App = () => {
const [state, stateDispatch] = useReducer(stateReducer,{loading: false})
useEffect(() => {
if(!state.loading) {
loadResults();
stateDispatch({type:'finishedLoadingResults'});
}
},[state.loading]);
if(notResults) {
stateDispatch({type:'loadingResults'});
}
return (
<ExampleComponent loading={state.loading} />
);
}
首先,你的 App 组件中的 useEffect 没有依赖列表,所以它在每次渲染时运行。 由于它设置了 state,这会导致新的重新渲染,之后您的 useEffect 会再次被调用,依此类推。所以您会得到一个无限循环。
您必须将依赖项列表添加到 useEffect。
Secondly, If your loadData function is an asynchronous function (which it should be if you're fetching data), it will return a promise, so you can dispatch "finishedLoadingResults" when the promise resolves.
这为您的 useEffect 提供了以下内容:
useEffect(() => {
stateDispatch({ type: "loadingResults" });
loadResults()
.then(() => stateDispatch({ type: "finishedLoadingResults" }));
}, []);
您可以在此 Sandbox中看到工作代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.