React文档中关于setState的内容如下:
If you need to set the state based on the previous state, read about the updater argument below
,
除了以下我不理解的句子:
如果正在使用可变对象,并且无法在 shouldComponentUpdate()中实现条件渲染逻辑,则仅当新状态不同于先前状态时才调用 setState()避免不必要的重新渲染。
他们说:
第一个参数是带有(state,props)=> stateChange 签名的 updater 函数... state 是对应用更改时的组件状态。
并举一个例子:
this.setState((state, props) => {
return {counter: state.counter + props.step};
});
说:
保证更新程序功能收到的状态和道具都是。更新器的输出与state进行浅层合并。
保证是最新的是什么意思,以及在决定是否应将setState
与更新程序功能(state, props) => stateChange
一起使用时应注意什么?还是直接以对象作为第一个参数?
让我们假设一个现实世界的场景。假设我们有一个精美的聊天应用程序,其中:
this.state = { messages: [] }
表示; messages
; messages
上。messages
处; 让我们假装这是我们的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
?我应该考虑什么?我应该始终使用updater
的{{1}}函数(为什么?)还是可以安全地直接将对象作为setState
参数(为什么?)传递?
感谢您的关注!
答案 0 :(得分:2)
setState
只关心 component 状态的一致性,而不关心服务器/客户端的一致性。因此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}
调用,并将状态设置为{counter: 0+1}
{counter: 1}
调用,并将状态设置为{counter: 1+1}
通常来说,更新程序功能是一种不太容易出错的更改状态的方法,很少有理由不使用它(尽管如果您设置的是静态值,则不一定要使用它)。
但是,您关心的是状态更新不会导致数据不正确(重复等)。在这种情况下,我会注意设计更新,使其无论数据的当前状态如何都是幂等且有效。例如,不要使用数组来保留消息的集合,而应使用映射,并通过该消息唯一的键或哈希存储每个消息,无论消息来自何处(毫秒时间戳可能足够唯一) 。然后,当您从两个位置获取相同的数据时,不会导致重复。
答案 1 :(得分:0)
我无论如何都不是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函数而不是对象。