[英]Cannot understand React setState signature "setState(updater[, callback])"
[英]Real world usage of setState with an updater callback instead of passing an object in React JS
React文檔說明了以下關於setState的內容 :
If you need to set the state based on the previous state, read about the updater argument below
,
除了以下句子,我不明白:
如果正在使用可變對象且無法在shouldComponentUpdate()中實現條件呈現邏輯,則僅當新狀態與先前狀態不同時調用setState()將避免不必要的重新呈現。
他們說:
第一個參數是帶有簽名的updater函數(state,props)=> stateChange ... state是對應用更改時組件狀態的引用。
舉個例子:
this.setState((state, props) => {
return {counter: state.counter + props.step};
});
他說:
更新程序功能接收的狀態和道具都保證是最新的 。 updater的輸出與state輕微合並。
在確定是否應該使用帶有更新程序功能的setState
(state, props) => stateChange
或直接將對象作為第一個參數時,它們是什么意思保證是最新的 ?我們應該注意什么?
讓我們假設一個現實世界的場景。 假設我們有一個花哨的聊天應用程序,其中:
this.state = { messages: [] }
; messages
; messages
中; messages
中,如第3點所示; 讓我們假裝這是我們的FancyChat
組件:
import React from 'react'
export default class FancyChat extends React.Component {
constructor(props) {
super(props)
this.state = {
messages: []
}
this.API_URL = 'http://...'
this.handleLoadPreviousChatMessages = this.handleLoadPreviousChatMessages.bind(this)
this.handleNewMessageFromOtherUser = this.handleNewMessageFromOtherUser.bind(this)
this.handleNewMessageFromCurrentUser = this.handleNewMessageFromCurrentUser.bind(this)
}
componentDidMount() {
// Assume this is a valid WebSocket connection which lets you add hooks:
this.webSocket = new FancyChatWebSocketConnection()
this.webSocket.addHook('newMessageFromOtherUsers', this.handleNewMessageFromOtherUser)
}
handleLoadPreviousChatMessages() {
// Assume `AJAX` lets you do AJAX requests to a server.
AJAX(this.API_URL, {
action: 'loadPreviousChatMessages',
// Load a previous chunk of messages below the oldest message
// which the client currently has or (`null`, initially) load the last chunk of messages.
below_id: (this.state.messages && this.state.messages[0].id) || null
}).then(json => {
// Need to prepend messages to messages here.
const messages = json.messages
// Should we directly use an updater object:
this.setState({
messages: messages.concat(this.state.messages)
.sort(this.sortByTimestampComparator)
})
// Or an updater callback like below cause (though I do not understand it fully)
// "Both state and props received by the updater function are guaranteed to be up-to-date."?
this.setState((state, props) => {
return {
messages: messages.concat(state.messages)
.sort(this.sortByTimestampComparator)
}
})
// What if while the user is loading the previous messages, it also receives a new message
// from the WebSocket channel?
})
}
handleNewMessageFromOtherUser(data) {
// `message` comes from other user thanks to the WebSocket connection.
const { message } = data
// Need to append message to messages here.
// Should we directly use an updater object:
this.setState({
messages: this.state.messages.concat([message])
// Assume `sentTimestamp` is a centralized Unix timestamp computed on the server.
.sort(this.sortByTimestampComparator)
})
// Or an updater callback like below cause (though I do not understand it fully)
// "Both state and props received by the updater function are guaranteed to be up-to-date."?
this.setState((state, props) => {
return {
messages: state.messages.concat([message])
.sort(this.sortByTimestampComparator)
}
})
}
handleNewMessageFromCurrentUser(messageToSend) {
AJAX(this.API_URL, {
action: 'newMessageFromCurrentUser',
message: messageToSend
}).then(json => {
// Need to append message to messages here (message has the server timestamp).
const message = json.message
// Should we directly use an updater object:
this.setState({
messages: this.state.messages.concat([message])
.sort(this.sortByTimestampComparator)
})
// Or an updater callback like below cause (though I do not understand it fully)
// "Both state and props received by the updater function are guaranteed to be up-to-date."?
this.setState((state, props) => {
return {
messages: state.messages.concat([message])
.sort(this.sortByTimestampComparator)
}
})
// What if while the current user is sending a message it also receives a new one from other users?
})
}
sortByTimestampComparator(messageA, messageB) {
return messageA.sentTimestamp - messageB.sentTimestamp
}
render() {
const {
messages
} = this.state
// Here, `messages` are somehow rendered together with an input field for the current user,
// as well as the above event handlers are passed further down to the respective components.
return (
<div>
{/* ... */}
</div>
)
}
}
有這么多的異步操作,我怎么能真正確定this.state.messages
將始終與服務器上的數據保持一致,以及如何為每種情況使用setState
? 我應該考慮什么? 我應該總是使用setState
的updater
函數(為什么?)或者直接傳遞一個對象作為updater
參數是安全的(為什么?)?
謝謝你的關注!
setState
僅關注組件狀態一致性,而不關心服務器/客戶端一致性。 因此, setState
不保證組件狀態與其他任何東西一致。
提供更新程序功能的原因是因為狀態更新有時會延遲,並且在調用setState
時不會立即發生。 因此,如果沒有更新程序功能,則基本上存在競爭條件。 例如:
state = {counter: 0}
開頭 this.setState({counter: this.state.counter +1})
this.setState({counter: 0+1})
,設置狀態為1次。 更新程序功能修復此問題,因為更新按順序應用:
state = {counter: 0}
開頭 this.setState((currentState, props) => ({counter: currentState.counter + 1}))
currentState.counter + 1
不會立即得到評估 {counter: 0}
調用第一個updater函數,並將狀態設置為{counter: 0+1}
{counter: 1}
調用第二個updater函數,並將狀態設置為{counter: 1+1}
一般來說,updater函數是更不容易出錯的改變狀態的方法,很少有理由不使用它(盡管如果你設置的是靜態值,你並不嚴格需要它)。
但是,您關心的是對狀態的更新不會導致不正確的數據(重復等)。 在這種情況下,我會注意更新的設計使它們無論數據的當前狀態如何都是冪等的並且可以工作。 例如,不是使用數組來保留消息集合,而是使用映射,並按照該消息所特有的鍵或散列存儲每條消息,無論它來自何處(毫秒時間戳可能足夠獨特) 。 然后,當您從兩個位置獲得相同的數據時,它不會導致重復。
我無論如何都不是React的專家而且只做了兩個月,但這是我從React的第一個項目中學到的東西,就像顯示隨機引用一樣簡單。
如果在使用setState后需要立即使用更新狀態,請始終使用updater函數。 讓我給你舉個例子。
//
handleClick = () => {
//get a random color
const newColor = this.selectRandomColor();
//Set the state to this new color
this.setState({color:newColor});
//Change the background or some elements color to this new Color
this.changeBackgroundColor();
}
我做了這個,發生的事情是,設置到正文的顏色始終是以前的顏色而不是狀態中的當前顏色,因為如您所知, setState
是批處理的。 當React認為最好執行它時會發生這種情況。 它沒有立即執行。 所以要解決這個問題,我所要做的就是將this.changeColor
作為setState的第二個參數傳遞。 因為這確保我應用的顏色與當前狀態保持同步。
所以要回答你的問題,在你的情況下,因為你的工作是在新消息到達時立即向用戶顯示消息,即使用UPDATED STATE,總是使用updater函數而不是對象。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.