假設我有這個簡單的虛擬組件:
const Component = () => {
const [state, setState] = useState(1);
setState(1);
return <div>Component</div>
}
在這段代碼中,我直接在組件主體中將狀態更新為與之前相同的值。但是,即使值保持不變,這也會導致過多的重新渲染。
而且據我所知,React.useState如果狀態值更新為與以前相同的值 - React 將不會重新渲染組件。那么為什么會在這里發生呢?
但是useEffect,如果我嘗試在組件主體中而不是直接在組件主體中做類似的事情:
const Component = () => {
const [state, setState] = useState(1);
useEffect(()=>{
setState(1);
},[state])
return <div>Component</div>
}
這不會導致任何無限回圈,并且完全符合如果狀態保持不變,React 不會重新渲染組件的規則。
所以我的問題是:useEffect為什么當我直接在組件主體中執行它而不在它沒有時會導致無限回圈?
如果有人對此有一些“幕后”的解釋,我將非常感激!
uj5u.com熱心網友回復:
TL;博士
第一個示例是無意的副作用,將無條件觸發重新渲染,而第二個示例是有意的副作用,并允許 React 組件生命周期按預期運行。
回答
我認為當 React 呼叫組件的 render 方法來計算下一個渲染周期的差異時,您將組件生命周期的“渲染階段”與我們通常在 React的“提交階段”期間稱為“渲染周期”的內容混為一談已更新 DOM。
查看組件生命周期圖:

請注意,在整個函式體都是“渲染”方法的 React 函陣列件中,函式的回傳值是我們想要重繪 或提交到 DOM 的值。現在我們都應該知道,React 組件的“渲染”方法被認為是一個沒有副作用的純函式。換句話說,渲染結果是 state 和 props 的純函式。
在第一個示例中,入隊狀態更新是在正常組件生命周期(即掛載、更新、卸載)之外呼叫的無意副作用。
const Component = () => {
const [state, setState] = useState(1);
setState(1); // <-- unintentional side-effect
return <div>Component</div>;
};
它在“渲染階段”觸發重新渲染。React 組件從來沒有機會完成渲染回圈,因此沒有什么可以“區分”或退出的,因此會發生渲染回圈。
入隊狀態更新的另一個例子是有意的副作用。在下一次 UI 更改重繪 或提交到 DOM后,useEffect掛鉤在渲染周期結束時運行。
const Component = () => {
const [state, setState] = useState(1);
useEffect(() => {
setState(1); // <-- intentional side-effect
}, [state]);
return <div>Component</div>;
}
useEffect鉤子大致是相當于類組件的componentDidMount、componentDidUpdate和componentWillUnmount生命周期方法的功能組件。無論依賴關系如何,都保證在組件安裝時至少運行一次。該效果將運行一次并將狀態更新排入佇列。React 將“看到”入隊的值與當前狀態值相同,并且不會觸發重新渲染。
同樣,您可以使用useEffect鉤子并完全洗掉依賴項陣列,這樣它就會/可以觸發每個渲染周期。
const Component = () => {
const [state, setState] = useState(1);
useEffect(() => {
setState(1);
});
return <div>Component</div>;
}
同樣,useEffect鉤子回呼保證至少被呼叫一次,將狀態更新入隊。React 將“看到”入隊的值與當前狀態值相同,并且不會觸發重新渲染。
這里的要點是不要將無意和意外的副作用編碼到您的 React 組件中,因為這會導致和/或導致錯誤代碼。
uj5u.com熱心網友回復:
呼叫時setState(1),您還會觸發重新渲染,因為這本質上是鉤子的作業方式。這是對底層機制的一個很好的解釋:
React.useState 觸發器如何重新渲染?
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/513829.html
