Javascript 状态更新可能是异步的

Javascript 状态更新可能是异步的,javascript,reactjs,asynchronous,setstate,Javascript,Reactjs,Asynchronous,Setstate,它们到底是什么意思?如果我理解正确,我无法在计算新状态时使用this.state,除非我将函数作为第一个参数传递给setState(): 但是我可以使用this.state来决定要做什么: if (this.state.a) this.setState({b: 2}); 道具呢 // Correct or wrong? this.setState({name: f(this.props)}); 在调用this.setState之后,我不能期望this.state改变: // Wron

它们到底是什么意思?如果我理解正确,我无法在计算新状态时使用
this.state
,除非我将函数作为第一个参数传递给
setState()

但是我可以使用
this.state
来决定要做什么:

if (this.state.a)
    this.setState({b: 2});
道具呢

// Correct or wrong?
this.setState({name: f(this.props)});
在调用
this.setState
之后,我不能期望
this.state
改变:

// Wrong
this.setState({a: f(this.state)});

// Correct
this.setState(prevState => {return {a: f(prevState)}});
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
。它将更改排入
此.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
的更改排队。

您并没有真正提出一个非常具体的问题。“这意味着什么”不太可能继续下去。但你似乎基本上理解了

调用
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
将根据调用
setState()
时的状态递增1,而在方法#2中,
foo
将根据arrow函数运行时的前一状态递增1。因此,如果在实际状态更新之前“同时”发生多个
setState()
调用,使用方法#1,它们可能会冲突和/或基于过时状态,而使用方法#2,它们保证具有最新状态,因为它们在状态更新阶段一个接一个地同步更新

以下是一个示例:


this.state.n
的最终值将是
1
。当
n
的值为
0
时,所有
setState()
调用都排队,因此它们都只需将其设置为
0+1


n
的最终值将是
3
。与方法#1一样,所有
setState()
调用都同时排队。但是,由于它们使用函数使用最新状态(包括并发状态更新对状态所做的更改)按顺序同步更新,因此它们正确地将
n
增加三倍



现在,为什么方法2的
console.log()
显示
3
三次,而不是
1
2
3
?答案是,
setState()
回调都发生在React中状态更新阶段的末尾,而不是在特定状态更新发生后立即发生。因此,在这方面,方法1和方法2是相同的。

请仔细检查您是否使用了redux或flux之类的状态管理框架?答案可能会有所不同,这取决于我要做什么。但是现在我想让它在没有redux的情况下工作。
// 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
// 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