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

时间:2017-03-24 07:43:06

标签: 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>
    )
  }
}

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

3 个答案:

答案 0 :(得分:17)

  

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

实例属性,例如在构造函数中设置this.state = {value: 0};。它使用当前位于第2阶段的Public Class Fields proposalincrementdecrement也是如此,它们是值为箭头函数的实例字段,因此它们会关闭this 。)

  

是一个局部变量吗?

没有

  

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

来自the documentation

  

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

     

由于this.propsthis.state可能会异步更新,因此您不应该依赖它们的值来计算下一个状态。

     

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

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});
     

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

// Correct
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

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

// Wrong
increment = () => {
  this.setState({
    value: this.state.value + 1
  });
};

...因为它依赖this.state的状态,上面告诉我们不要这样做;所以引用的代码改为:

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

以下证明React可能以非显而易见的方式批量调用,以及为什么我们需要使用setState的回调版本:在这里,我们有increment和{{1}每次点击被叫两次,而不是一次(一次是按钮,一次是包含按钮的跨度)。点击decrement 一次会将计数器增加到2,因为+会被调用两次。但是因为我们还没有使用increment的函数回调版本,所以它没有:其中一个对setState的调用变为无操作,因为我们正在使用陈旧的increment值:

&#13;
&#13;
this.state.value
&#13;
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")
);
&#13;
&#13;
&#13;

使用函数回调,它可以正常工作(没有调用<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>成为无操作):

&#13;
&#13;
increment
&#13;
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")
);
&#13;
&#13;
&#13;

在这种情况下,当然,我们可以查看<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>方法并说出&#34;嘿,render会在点击期间被调用两次,我最好使用回调版increment。&#34;但是,在确定下一个状态时,不要假设使用setState是安全的,最佳做法是来假设。在一个复杂的组件中,它是一种易于使用的mutator方法,其方式是mutator方法的作者可能没有想到的。因此,React作者的陈述:

  

由于this.statethis.props可能会异步更新,您不应该依赖其值来计算下一个状态

答案 1 :(得分:3)

  

关于问题2,请参阅Dan在这里的好答案:Do I need to use setState(function) overload in this case?

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

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

  3. 但请注意,通过使用功能性setState,您可以获得一些好处:

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

      const increment = prevState => ({
        value: prevState.value + 1
      })
      

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

      this.setState(increment)
      
    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 })
      }
      

      执行此功能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)
      

      将按预期计算+3。

    3. 您可以在此处查看文档:{​​{3}}

答案 2 :(得分:1)

问题1:

是的,您可以在没有React类组件的构造函数的情况下初始化状态:

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

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

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

  render() {
    return (
      <div>
        {this.state.value}

        <button onClick={this.handleIncrement}>+</button>
        <button onClick={this.handleDecrement}>-</button>
      </div>
    )
  }
}

它使用类字段声明。您可以通过here检出示例应用程序。您还可以在React函数组件中使用状态(不使用构造函数),但可以使用高阶组件或渲染prop组件。您可以找到有关here的更多信息。

问题2的答案:有两种方法可以使用this.setState更新React中的本地状态。您可以使用对象或函数。如果您不依赖组件的属性或状态来更新其状态,则该对象就很好。如果您依赖道具或状态,则应改用函数,因为函数在执行时始终具有最新的道具和状态。这是因为this.setState()是异步执行的,因此在使用对象而不是函数更新本地状态时,可以使用陈旧状态或道具。您可以详细了解here