[英]Real world usage of setState with an updater callback instead of passing an object in React JS
The React documentation says the following about setState : React文档说明了以下关于setState的内容 :
If you need to set the state based on the previous state, read about the updater argument below
, If you need to set the state based on the previous state, read about the updater argument below
,
Beside the following sentence, which I don't understand: 除了以下句子,我不明白:
If mutable objects are being used and conditional rendering logic cannot be implemented in shouldComponentUpdate() , calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.
如果正在使用可变对象且无法在shouldComponentUpdate()中实现条件呈现逻辑,则仅当新状态与先前状态不同时调用setState()将避免不必要的重新呈现。
They say: 他们说:
The first argument is an updater function with the signature (state, props) => stateChange ... state is a reference to the component state at the time the change is being applied.
第一个参数是带有签名的updater函数(state,props)=> stateChange ... state是对应用更改时组件状态的引用。
And make an example: 举个例子:
this.setState((state, props) => {
return {counter: state.counter + props.step};
});
Saying: 他说:
Both state and props received by the updater function are guaranteed to be up-to-date .
更新程序功能接收的状态和道具都保证是最新的 。 The output of the updater is shallowly merged with state.
updater的输出与state轻微合并。
What do they mean by guaranteed to be up-to-date and what should we be aware of when deciding if we should use setState
with an updater function (state, props) => stateChange
or directly with an object as the first parameter? 在确定是否应该使用带有更新程序功能的
setState
(state, props) => stateChange
或直接将对象作为第一个参数时,它们是什么意思保证是最新的 ?我们应该注意什么?
Let's assume a real world scenario. 让我们假设一个现实世界的场景。 Suppose we have a fancy chat application where:
假设我们有一个花哨的聊天应用程序,其中:
this.state = { messages: [] }
; this.state = { messages: [] }
; messages
currently in the state; messages
; messages
currently in the state; messages
中; messages
of the state as in point 3 as soon as the AJAX request fired when the message is sent completes; messages
中,如第3点所示; Let's pretend this is our FancyChat
component: 让我们假装这是我们的
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>
)
}
}
With so many asynchronous operations, how can I be really sure that this.state.messages
will always be consistent with the data on the server and how would I use setState
for each case? 有这么多的异步操作,我怎么能真正确定
this.state.messages
将始终与服务器上的数据保持一致,以及如何为每种情况使用setState
? What considerations I should make? 我应该考虑什么? Should I always use the
updater
function of setState
(why?) or is safe to directly pass an object as the updater
parameter (why?)? 我应该总是使用
setState
的updater
函数(为什么?)或者直接传递一个对象作为updater
参数是安全的(为什么?)?
Thank you for the attention! 谢谢你的关注!
setState
is only concerned with component state consistency, not server/client consistency. setState
仅关注组件状态一致性,而不关心服务器/客户端一致性。 So setState
makes no guarantees that the component state is consistent with anything else. 因此,
setState
不保证组件状态与其他任何东西一致。
The reason an updater function is provided, is because state updates are sometimes delayed, and don't occur immediately when setState
is called. 提供更新程序功能的原因是因为状态更新有时会延迟,并且在调用
setState
时不会立即发生。 Therefore, without the updater function, you have essentially a race condition. 因此,如果没有更新程序功能,则基本上存在竞争条件。 For example:
例如:
state = {counter: 0}
state = {counter: 0}
开头 this.setState({counter: this.state.counter +1})
this.setState({counter: this.state.counter +1})
this.setState({counter: 0+1})
, setting the state to 1 both times. this.setState({counter: 0+1})
,设置状态为1次。 An updater function fixes this, because the updates are applied in order: 更新程序功能修复此问题,因为更新按顺序应用:
state = {counter: 0}
state = {counter: 0}
开头 this.setState((currentState, props) => ({counter: currentState.counter + 1}))
this.setState((currentState, props) => ({counter: currentState.counter + 1}))
currentState.counter + 1
does not get evaluated immediately currentState.counter + 1
不会立即得到评估 {counter: 0}
, and sets the state to {counter: 0+1}
{counter: 0}
调用第一个updater函数,并将状态设置为{counter: 0+1}
{counter: 1}
, and sets the state to {counter: 1+1}
{counter: 1}
调用第二个updater函数,并将状态设置为{counter: 1+1}
Generally speaking, the updater function is the less error-prone way to change the state, and there is rarely a reason to not use it (although if you are setting a static value, you don't strictly need it). 一般来说,updater函数是更不容易出错的改变状态的方法,很少有理由不使用它(尽管如果你设置的是静态值,你并不严格需要它)。
What you care about, however, is that updates to the state don't cause improper data (duplicates and the like). 但是,您关心的是对状态的更新不会导致不正确的数据(重复等)。 In that case, I would take care that the updates are designed so that they are idempotent and work no matter the current state of the data.
在这种情况下,我会注意更新的设计使它们无论数据的当前状态如何都是幂等的并且可以工作。 For instance, instead of using an array to keep the collection of messages, use a map instead, and store each message by key or hash that is unique to that message, no matter where it came from (a millisecond timestamp may be unique enough).
例如,不是使用数组来保留消息集合,而是使用映射,并按照该消息所特有的键或散列存储每条消息,无论它来自何处(毫秒时间戳可能足够独特) 。 Then, when you get the same data from two locations, it will not cause duplicates.
然后,当您从两个位置获得相同的数据时,它不会导致重复。
I'm not an expert in React by any means and have only been doing it for two months only, but here's what I learned from my very first project in React, which was as simple as showing a random quote. 我无论如何都不是React的专家而且只做了两个月,但这是我从React的第一个项目中学到的东西,就像显示随机引用一样简单。
If you need to use the updated state right after you use setState, always use the updater function. 如果在使用setState后需要立即使用更新状态,请始终使用updater函数。 Let me give you an example.
让我给你举个例子。
//
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();
}
I did this and what happened was that the color that was set to the body was always the previous color and not the current color in the state, because as you know, the setState
is batched. 我做了这个,发生的事情是,设置到正文的颜色始终是以前的颜色而不是状态中的当前颜色,因为如您所知,
setState
是批处理的。 It happens when React thinks it's best to execute it. 当React认为最好执行它时会发生这种情况。 It's not executed immediately.
它没有立即执行。 So to solve this problem, all I have to do was pass
this.changeColor
as a second argument to setState. 所以要解决这个问题,我所要做的就是将
this.changeColor
作为setState的第二个参数传递。 Because that ensured that the color I applied was kept up to date with the current state. 因为这确保我应用的颜色与当前状态保持同步。
So to answer your question, in your case, since you're job is to display the message to the user as soon as a new message arrives, ie use the UPDATED STATE, always use the updater function and not the object. 所以要回答你的问题,在你的情况下,因为你的工作是在新消息到达时立即向用户显示消息,即使用UPDATED STATE,总是使用updater函数而不是对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.