初始状态没有构造函数的反应

Jen*_*Mok 16 javascript reactjs

import React, { Component } from 'react';

class Counter extends Component {
  state = { value: 0 };

  increment = () => {
    this.setState(prevState => ({
      value: prevState.value + 1
    }));
  };

  decrement = () => {
    this.setState(prevState => ({
      value: prevState.value - 1
    }));
  };

  render() {
    return (
      <div>
        {this.state.value}
        <button onClick={this.increment}>+</button>
        <button onClick={this.decrement}>-</button>
      </div>
    )
  }
}
Run Code Online (Sandbox Code Playgroud)

通常我看到的是人们在构造函数中执行this.state,如果他使用es6类.如果他不是,他可能使用getinitialstate函数放置状态.但是上面的代码(是的,它是一个工作代码),没有使用这两个.我有2个问题,这里的状态是什么?是一个局部变量?如果是,为什么它没有const?prevState来自哪里?为什么在setState中使用箭头函数?这不是很容易this.setState({value:'something'})吗?

T.J*_*der 17

我有两个问题,这是state什么?

实例属性,如this.state = {value: 0};在构造函数中设置.它使用的公共类字段建议目前阶段2(所以是incrementdecrement,这是实例字段,其值是箭头功能,所以他们关闭了this.)

是一个局部变量?

没有.

prevState来自哪里?为什么在setState中使用箭头函数?是不是很容易做到这一点.setState({value:'something'})?

文档:

React可以将多个setState()调用批处理为单个更新以提高性能.

因为this.props并且this.state可以异步更新,所以不应该依赖它们的值来计算下一个状态.

例如,此代码可能无法更新计数器:

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});
Run Code Online (Sandbox Code Playgroud)

要修复它,请使用第二种形式setState()接受函数而不是对象.该函数将接收先前的状态作为第一个参数,并将更新作为第二个参数应用于道具:

// Correct
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));
Run Code Online (Sandbox Code Playgroud)

...这正是引用代码正在做的事情.这是错误的:

// Wrong
increment = () => {
  this.setState({
    value: this.state.value + 1
  });
};
Run Code Online (Sandbox Code Playgroud)

...因为它依赖于this.state以上告诉我们的状态; 所以引用的代码改为:

increment = () => {
  this.setState(prevState => ({
    value: prevState.value + 1
  }));
};
Run Code Online (Sandbox Code Playgroud)

这里的证明,以非显而易见的方式作出反应5批调用,为什么我们需要使用回调版本setState:在这里,我们incrementdecrement被调用两次通过包含按钮跨度每次点击而不是由按钮一次(一次,连用).单击+ 一次应将计数器增加到2,因为increment调用两次.但是因为我们没有使用函数回调版本setState,所以它没有:其中一个调用increment成为无操作,因为我们使用的是陈旧this.state.value值:

class Counter extends React.Component {
  state = { value: 0 };

  increment = () => {
    /*
    this.setState(prevState => ({
      value: prevState.value + 1
    }));
    */
    console.log("increment called, this.state.value = " + this.state.value);
    this.setState({
      value: this.state.value + 1
    });
  };
  
  fooup = () => {
    this.increment();
  };

  decrement = () => {
    /*
    this.setState(prevState => ({
      value: prevState.value - 1
    }));
    */
    console.log("decrement called, this.state.value = " + this.state.value);
    this.setState({
      value: this.state.value - 1
    });
  };
  
  foodown = () => {
    this.decrement();
  };

  render() {
    return (
      <div>
        {this.state.value}
        <span onClick={this.fooup}>
          <button onClick={this.increment}>+</button>
        </span>
        <span onClick={this.foodown}>
          <button onClick={this.decrement}>-</button>
        </span>
      </div>
    )
  }
}

ReactDOM.render(
  <Counter />,
  document.getElementById("react")
);
Run Code Online (Sandbox Code Playgroud)
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

使用函数回调,它可以正常工作(没有调用increment成为no-ops):

class Counter extends React.Component {
  state = { value: 0 };

  increment = () => {
    this.setState(prevState => {
      console.log("Incrementing, prevState.value = " + prevState.value);
      return {
        value: prevState.value + 1
      };
    });
  };
  
  fooup = () => {
    this.increment();
  };

  decrement = () => {
    this.setState(prevState => {
      console.log("Decrementing, prevState.value = " + prevState.value);
      return {
        value: prevState.value - 1
      };
    });
  };
  
  foodown = () => {
    this.decrement();
  };

  render() {
    return (
      <div>
        {this.state.value}
        <span onClick={this.fooup}>
          <button onClick={this.increment}>+</button>
        </span>
        <span onClick={this.foodown}>
          <button onClick={this.decrement}>-</button>
        </span>
      </div>
    )
  }
}

ReactDOM.render(
  <Counter />,
  document.getElementById("react")
);
Run Code Online (Sandbox Code Playgroud)
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

在这种情况下,当然,我们可以查看该render方法并说"嘿,increment在点击期间会被调用两次,我最好使用回调版本setState." 但是,不是假设this.state在确定下一个状态时使用它是安全的,最佳做法是不要假设.在一个复杂的组件中,很容易以mutator方法的作者可能没有想到的方式使用mutator方法.因此,React作者的声明:

因为this.props并且this.state可以异步更新,所以不应该依赖它们的值来计算下一个状态.

  • @JennyMok:"没错" - 是的,有.仅仅因为它还没有咬你,这并不意味着它没有任何问题.请参阅文档.React开发人员可能比我们更了解React,并且不只是添加这个形式接受一个奇思妙想的函数. (3认同)

Cod*_*Cat 7

关于问题 2,请参考 Dan 的精彩回答:在这种情况下,我需要使用 setState(function) 重载吗?


  1. 不,它不是局部变量。这与this.state在构造函数中声明相同。

  2. 是的,在这种情况下,您可以只使用this.setState({ value: this.state.value + 1 }),结果将是相同的。

但请注意,通过使用函数式,setState您可以获得一些好处:

  1. 如果在外部声明 setState 函数,则可以重用它:

    const increment = prevState => ({
      value: prevState.value + 1
    })
    
    Run Code Online (Sandbox Code Playgroud)

    现在如果你有几个组件需要使用这个功能,你可以在任何地方导入和重用逻辑。

    this.setState(increment)
    
    Run Code Online (Sandbox Code Playgroud)
  2. React 会压缩几个setState并批量执行它们。这可能会导致一些意外行为。请看下面的例子:

    http://codepen.io/CodinCat/pen/pebLaZ

    add3 () {
      this.setState({ count: this.state.count + 1 })
      this.setState({ count: this.state.count + 1 })
      this.setState({ count: this.state.count + 1 })
    }
    
    Run Code Online (Sandbox Code Playgroud)

    执行这个函数count只会加1

    如果您使用函数式 setState:

    http://codepen.io/CodinCat/pen/dvXmwX

    const add = state => ({ count: state.count + 1 })
    this.setState(add)
    this.setState(add)
    this.setState(add)
    
    Run Code Online (Sandbox Code Playgroud)

    如您所料,计数将 +3。

您可以在此处查看文档:https : //facebook.github.io/react/docs/react-component.html#setstate