![](/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.