![](/img/trans.png)
[英]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.