Обновления состояний могут быть асинхронными
Что именно они имеют в виду? Если я правильно понимаю, я не могу использовать this.state
при вычислении нового состояния, если я не передам функцию в качестве первого параметра setState()
:
// Wrong
this.setState({a: f(this.state)});
// Correct
this.setState(prevState => {return {a: f(prevState)}});
Но я могу использовать this.state
решить, что делать:
if (this.state.a)
this.setState({b: 2});
А как насчет реквизита?
// Correct or wrong?
this.setState({name: f(this.props)});
И я не могу ожидать this.state
поменять после звонка this.setState
:
this.setState({a: 1});
console.log(this.state.a); // not necessarily 1
Затем, скажем, у меня есть список пользователей. И выберите, где я могу сделать текущий пользователь одним:
export default class App extends React.Component {
...
setCurrentUserOption(option) {
this.setState({currentUserOption: option});
if (option)
ls('currentUserOption', option);
else
ls.remove('currentUserOption');
}
handleAddUser(user) {
const nUsers = this.state.users.length;
this.setState(prevState => {
return {users: prevState.users.concat(user)};
}, () => {
// here we might expect any number of users
// but if first user was added, deleted and added again
// two callbacks will be called and setCurrentUserOption
// will eventually get passed a correct value
// make first user added current
if ( ! nUsers)
this.setCurrentUserOption(this.userToOption(user));
});
}
handleChangeUser(user) {
this.setState(prevState => {
return {users: prevState.users.map(u => u.id == user.id ? user : u)};
}, () => {
// again, we might expect any state here
// but a sequence of callback will do the right thing
// in the end
// update value if current user was changed
if (_.get(this.state, 'currentUserOption.value') == user.id)
this.setCurrentUserOption(this.userToOption(user));
});
}
handleDeleteUser(id) {
this.setState(prevState => {
return {users: _.reject(prevState.users, {id})};
}, () => {
// same here
// choose first user if current one was deleted
if (_.get(this.state, 'currentUserOption.value') == id)
this.setCurrentUserOption(this.userToOption(this.state.users[0]));
});
}
...
}
Все ли обратные вызовы выполняются последовательно после применения пакета изменений состояния?
На второй мысли, setCurrentUserOption
в основном как setState
, Это ставит в очередь изменения в this.state
, Даже если обратные вызовы вызываются последовательно, я не могу положиться на this.state
может быть изменен предыдущим обратным вызовом, могу я? Так что может быть лучше не извлекать setCurrentUserOption
метод:
handleAddUser(user) {
const nUsers = this.state.users.length;
this.setState(prevState => {
let state = {users: prevState.users.concat(user)};
if ( ! nUsers) {
state['currentUserOption'] = this.userToOption(user);
this.saveCurrentUserOption(state['currentUserOption']);
}
return state;
});
}
saveCurrentUserOption(option) {
if (option)
ls('currentUserOption', option);
else
ls.remove('currentUserOption');
}
Таким образом, я получаю очередь изменений в currentUserOption
бесплатно.
1 ответ
Вы действительно не задавали очень конкретный вопрос. "Что это значит" не так уж и много. Но, как правило, вы, кажется, понимаете основы.
Есть два возможных способа позвонить setState()
: либо путем передачи ему объекта для слияния в новое состояние, либо путем передачи ему функции, которая возвращает объект, который сливается способом, подобным первому способу.
Так что вы либо делаете это:
// Method #1
this.setState({foo: this.state.foo + 1}, this.someCallback);
Или это:
// Method #2
this.setState((prevState) => {return {foo: prevState.foo + 1}}, this.someCallback);
Основное отличие состоит в том, что при методе № 1 foo
будет увеличен на 1 в зависимости от состояния, в котором он находился во время вызоваsetState()
в то время как в методе № 2, foo
будет увеличен на 1 в зависимости от того, каким было предыдущее состояние в момент запуска функции стрелки. Так что если у вас есть несколько setState()
вызовы, которые происходят в "то же самое время" перед фактическим обновлением состояния, при методе № 1 они могут конфликтовать и / или основываться на устаревшем состоянии, в то время как при методе № 2 они гарантированно будут иметь самое последнее состояние, поскольку они обновляются синхронно, один за другим, на этапе обновления состояния.
Вот иллюстративный пример:
// Method #1
class App extends React.Component {
constructor(props) {
super(props);
this.state = {n: 0};
this.increment.bind(this);
}
componentDidMount() {
this.increment();
this.increment();
this.increment();
}
increment() {
this.setState({n: this.state.n + 1}, () => {console.log(this.state.n)});
}
render() {
return (
<h1>{this.state.n}</h1>
);
}
}
React.render(
<App />,
document.getElementById('react_example')
);
В приведенном выше: вы увидите это в консоли:
> 1
> 1
> 1
И окончательная стоимость this.state.n
было бы 1
, Все setState()
звонки были поставлены в очередь, когда значение n
было 0
так что все они просто установить его 0 + 1
,
// Method #2
class App extends React.Component {
constructor(props) {
super(props);
this.state = {n: 0};
this.increment.bind(this);
}
componentDidMount() {
this.increment();
this.increment();
this.increment();
}
increment() {
this.setState((prevState) => {return {n: prevState.n + 1}}, () => {console.log(this.state.n)});
}
render() {
return (
<h1>{this.state.n}</h1>
);
}
}
React.render(
<App />,
document.getElementById('react_example')
);
Выше вы увидите это в консоли:
> 3
> 3
> 3
И окончательная стоимость n
было бы 3
, Как и в методе № 1, все setState()
звонки были поставлены в очередь одновременно. Тем не менее, поскольку они используют функцию для синхронного обновления в порядке, используя самое актуальное состояние - включая изменения состояния, сделанные одновременными обновлениями состояния - они должным образом увеличивают n
три раза, как и следовало ожидать.
Теперь, почему console.log()
шоу 3
три раза для метода № 2 вместо 1
, 2
, 3
? Ответ в том, что setState()
Все обратные вызовы происходят вместе в конце фазы обновления состояния в React, а не сразу после того, как происходит это обновление конкретного состояния. Так что в этом отношении методы № 1 и № 2 идентичны.