Tom*_*uer 9 javascript typescript reactjs
我知道this.state不应该直接修改,而setState应该使用.
从这里我推断,prevState应该也被视为不可变的,而setState应该总是看起来像这样:
this.setState((prevState) => {
  // Create a new object instance to return
  const state = { ...prevState };
  state.counter = state.counter + 1;
  return state;
});
或者更深的嵌套:
this.setState((prevState) => {
  // Create a new object instance to return
  const state = { ...prevState, counter: { ...prevState.counter } };
  state.counter.value = state.counter.value + 1;
  return state;
});
或者只是部分更新,比如setState({})更容易和更好用的地方:
this.setState((prevState) => ({ counter: prevState.counter + 1 }));
所有上述内容显然是正确的,因为它们返回一个新实例,但后来我遇到了这个问题,其中接受的答案鼓励变异prevState而不返回新的对象实例(注意问题中的代码块).
像这样的东西:
this.setState((prevState) => {
  prevState.flag = !prevState.flag;
  return prevState;
});
我发现这是一个粗略的建议,所以我决定测试对象实例引用prevState和this.state是否相同:
class Test extends React.Component {
  state = { counter: 0 };
  onDemonstrateButtonClick = (event) => {
    this.setState((prevState) => {
      if (prevState === this.state) alert(`uh, yep`);
      prevState.counter++;
      return prevState;
    });
  };
  render() {
    return (
      <div>
        <button onClick={this.onDemonstrateButtonClick}>Demonstrate</button>
        {this.state.counter}
      </div>
    );
  }
}
Whadayaknow,他们是!那是哪个呢?答案是错的,我应该返回一个新的对象实例还是将部分更新作为一个新对象返回,还是可以prevState直接改变参数?如果是的话,这与this.state直接变异有何不同?
旁注:TypeScript React类型不会将参数标记为ReadOnly只会增加我的困惑.
Amr*_*bib 10
是否可以将setState函数的prevState参数视为可变?
答案是否定的,你永远不应该变异prevState,这在setState部分的反应文档中也有明确提及
prevState是对先前状态的引用.它不应该直接变异.相反,应该通过基于来自prevState和props的输入构建新对象来表示更改.
你测试了prevState,this.state它们是相同的,实际上它们不是.
要弄清楚为什么它们实际上是不同的,我们需要知道为什么prevState实际存在,答案是setState函数是异步的,这就是为什么反应js给我们访问权限prevState让我们检查下面的例子prevState != this.state
在下面的例子中,我们将每次点击增加计数器两次,但我们将使用2个setState操作,每个操作将计数器增加1.
因为setState是async你会发现,第二setState操作开始之前先setState完成这就是prevState是有用的,在这里prevState和this.state不相等.
我用一个数字来表示每一行,表示执行这一行时,这应该解释我们为什么需要prevState以及为什么它不同this.state.
class App extends React.Component{
  constructor(props)
  {
    super(props);
    this.state = {
      counter : 1
    };
  }
  increment = () =>{
    this.setState((prevState , props) => {
      console.log("in first"); //3
      console.log(prevState);  //3
      console.log(this.state); //3
      if(prevState == this.state)
      {
         console.log("in first prevState == this.state");
      }
      return {
        counter : prevState.counter+1
      }
    } , ()=>{
      console.log("done updating first"); //5
    });
    console.log("after first"); //1
    this.setState((prevState, props) => {
      console.log("in second"); //4
      console.log(this.state);  //4
      console.log(prevState);   //4
      if (prevState == this.state) {
        console.log("in second prevState == this.state");
      }
      return {
        counter: prevState.counter + 1
      }
    } , () =>{
      console.log("done updating second"); //6
    });
    console.log("after second"); //2
  }
  render(){
    return (
      <div>
        <span>{this.state.counter}</span>
        <br/>
        <button onClick={this.increment} >increment</button>
      </div>
    )
  }
}
上面代码的结果是
"after first"
"after second"
"in first"
?Object {counter: 1}
?Object {counter: 1}
"in first prevState == this.state"
"in second"
?Object {counter: 1}
?Object {counter: 2}
"done updating first"
"done updating second"
上面的代码完全在这个链接中工作,你可以查看console.log结果 https://codesandbox.io/s/k325l485mr
上面的例子将在每次点击时正确地增加计数器两次,如果你想在第二次中断它改变return语句 setState 
从
return {
    counter: prevState.counter + 1
}
至
return {
    counter: this.state.counter + 1
}
并且你会发现结果不正确每次点击都会导致1个增量,这是不正确的,因为我们有2 setState,这是因为我们没有使用prevState,我们使用了不正确this.state
我相信更新计数器的正确方法是
this.setState((prevState) => ({ counter: prevState.counter + 1 }));
| 归档时间: | 
 | 
| 查看次数: | 3127 次 | 
| 最近记录: |