[英]React JS setState behaviour
我正在學習本教程 ,並在1:10:18(鏈接中包含時間步長)上,他寫了this.setState({ count: this.state.count + 1 });
(其中count是狀態對象內的原始值,單擊按鈕后將顯示在頁面上)從導師的話中,我了解到我們需要在setState內部指定count:
以便React將在狀態對象中更新此值。 但是我注意到我可以在setState中寫一些廢話,例如:
handleIncrement = key => {
this.state.totalCount += 1;
this.state.tags[key] += 1;
this.setState({ countBlabla: 23213123 });
};
而且HTML仍將正確更新,而無需重新呈現所有內容。 (我已經簽入開發人員工具,只有已更改的值會更新,其他所有內容都保持不變)
我也嘗試過: this.setState({});
效果很好。 但是this.setState();
才不是。
那么將setState傳遞給參數有什么意義呢?
您可以直接寫入this.state
唯一位置應該是Components構造函數。 在所有其他地方,您應該使用this.setState
函數,該函數將接受一個Object,該Object最終將合並到Components當前狀態。
盡管在技術上可以通過直接寫入this.state
來更改狀態,但是這不會導致使用新數據重新渲染組件,並且通常會導致狀態不一致。
另外, this.setState()
是一個異步函數,因此,如果您只是在調用新狀態后嘗試console.log()
新狀態,就不會立即看到更改。 相反,您可以傳遞一個回調函數this.setState({var: newVar}, () => {console.log(this.state)})
。
遵循以下這行react軟件包的代碼:
setState
方法只調用enqueueSetState
方法。 而這又使特定反應組件的更新入隊。 更新是通過react-dom
包(或react-native
)處理的
如果您看一下forceUpdate
方法,您會發現它實際上在做同樣的事情(調用enqueueSetState
)。 而且此方法僅觸發對帳過程。
因此,簡而言之,沒有必要從純觸發前瞻性的狀態中改變狀態,也可以不改變狀態。 但是從優化和清晰的數據流角度出發,這是非常必要的。 如果在使用setState
時依賴state
引用,則當某些組件看到舊狀態時,您可能會遇到棘手的行為。
您只需要傳遞需要更新的值。 React會將當前狀態傳播到新狀態。 如果您現在的狀態為{ count: 1, prop: 2 }
:
// if you will make
this.setState({ count: 11 })
// react will do
{ ...currentState, count: 11 }
// and result will be
{ prop: 2, count: 11 }
並通過調用對帳過程請求更新組件。 就是這樣,不要忘記:
PS 這是Dan Abramov(React團隊的核心成員)的精彩文章
通常,您永遠不想直接改變狀態。 始終使用setState
。
在回答你的問題,為什么我們需要指定鍵(在您的示例數 ):因為這是我們要改變。 setState不僅會重新渲染組件,而且我們也不只是為了重新渲染而指定鍵。 它更改狀態,然后重新渲染組件,以便可以使用狀態中的新值。
從技術上講,您可以forceUpdate
狀態並使用隨機參數或對象調用forceUpdate
或setState
,但您不應這樣做,因為這會破壞React組件設計的自然工作流程。
如果將對象傳遞給setState,它將在內部合並該對象並導致重新渲染;如果未傳遞任何內容,它將跳過該重新渲染,這是您觀察到的行為
現在讓我們看一下上面代碼的缺點
handleIncrement = key => {
this.state.totalCount += 1;
this.state.tags[key] += 1;
this.setState({ countBlabla: 23213123 });
};
在上述情況下,假設您將totalCount和標簽作為道具傳遞給子組件,並基於要進行API調用的totalCount更改或標簽更改在子組件中傳遞。 根據React流,您可以在componentDidUpdate生命周期方法中執行此操作
componentDidUpdate(prevProps) {
if (prevProps.totalCount !== this.props.totalCount) {
// make api cal;
}
}
但是,盡管以上代碼在totalCount為整數,布爾值或字符串的情況下都可以使用,因為它們是不可變的,但不適用於對象。 這樣的情況將很難調試,並可能導致意外的行為。
您正在做的事情叫做巧合編程。
您的代碼恰好由於react的內部工作而起作用,但是它是未記錄的行為,將來可能會在沒有警告的情況下停止工作。
建議的方法是使用this.setState({...})
並傳遞對狀態所做的所有更改的對象。 您的情況是:
this.setState({
totalCount: this.state.totalCount + 1
tags: {
...this.state.tags,
[key]: this.state.tags[key] + 1
}
});
這將更新狀態並獲得與您的代碼相同的副作用。
但是 ,將當前狀態視為不可變是很重要的,因為例如,如果將來某個時候您決定實施shouldComponentUpdate
方法並拒絕狀態更改(如果手動更改了狀態),由於先前的不一致,將會產生一些意想不到的后果陳述您的期望。
除其他答案外,如果要訪問先前的狀態,還可以將其作為參數傳遞給setState。
例如:
this.setState((prevState) => ({
totalCount: prevState.totalCount + 1
tags: {
...prevState.tags,
[key]: prevState.tags[key] + 1
}
}));
建議這樣做,因為此狀態可能會異步更新。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.