javascript - react 中没有构造函数的初始状态

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

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

最佳答案

I have 2 question, what is state here?

实例属性,例如在构造函数中设置 this.state = {value: 0};。它使用 Public Class Fields proposal目前处于阶段 2。(incrementdecrement 也是如此,它们是实例字段,其值是箭头函数,因此它们关闭 this。)

is that a local variable?

没有。

where does the prevState come from? why arrow function is used in setState? isn't it's easy to just do this.setState({value:'something'})?

来自 the documentation :

React may batch multiple setState() calls into a single update for performance.

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.

For example, this code may fail to update the counter:

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

To fix it, use a second form of setState() that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument:

// 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 的回调版本:这里,我们有 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")
);
<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>

通过函数回调,它可以正常工作(没有调用 increment 变成空操作):

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")
);
<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>

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

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.

关于javascript - react 中没有构造函数的初始状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42993989/

相关文章:

javascript - firebase.auth.currentUser 返回 null 直到在表单中输入内容

javascript - 将多个参数传递给 Promise 函数?

javascript - 如何使用嵌套路由向页面添加内容而不用 react-router-v4 删除先前路由的内容?

reactjs - 类型错误 : Cannot call a class as a function in stenciljs

javascript - 如果我们给出超过 16 位的数字,如何获得正确的整数值?

javascript - 将 "resolve"放在 Angular-ui-router View 上不起作用 - 页面根本无法渲染

javascript - react 调用子方法的不同方式

css - 如何删除 material-ui 的可滚动选项卡中的额外空间?

javascript - 直接访问路由器 URL 不起作用

javascript - 如何将 Knockout View 模型移到 html 页面之外