乐趣区

关于javascript:props-state

一、React 的 state 和 props 的概念

1.props

props 是组件本身的属性(不可变),是一种父级向子级传递数据的形式

2.state:

state 是组件持有的状态(可变),React 中组件的一个对象。state 只能由组件自身进行治理,组件会依据 state 的变动进行从新渲染。

二、React 的 state 和 props 的区别

1. 组件本身是能够创立 state 的,这个 state 是由组件本身去进行保护的,props 能够了解为组件的属性,是由父组件传递下来的,子组件能够通过 this.props 来获取到父组件传递的数据流。

父组件能够通过 props 向子组件进行数据的传递:

// 父组件通过 props 传值
class Container extends React.Component {constructor(props) {super(props); // 假如传进来的 props 中有 name
  }

  render() {
    return (
      <div>
        <Component name={this.props.name} />
        <Component name={this.props.name} />
        <Component name={this.props.name} />
      </div>
    );
  }
}

Container 组件中蕴含着 3 个 Component 组件,在传参过程中 Component 的 name 值都是由 Container 传递的,就是说每个组件的 props 都是由父组件传递过去的,因为本身是没方法创立 props 属性的,而 state 属性则是能够本身创立的,批改下例子也一样能够用:

// 组件本身创立 state
class Container extends React.Component {constructor(props) {super(props);

    this.state = {name: "huangdonglu"}
  }

  render() {
    return (
      <div>
        <Component name={this.state.name} />
        <Component name={this.state.name} />
        <Component name={this.state.name} />
      </div>
    );
  }
}

这阐明父组件创立的 state 值能够通过 props 传递给子组件。

2.state 能够通过 setState 批改,一个组件本身能够治理本人的 state(相似于一个在函数外部申明的变量),然而无奈治理其子组件的 state,所以 state 是控件本身公有的。从子组件的角度来看,props 是不能够被批改的,所有 React 组件都必须像纯函数一样爱护它们的 props 不被更改,即不能调用 this.props.name = xxx 来对 props 批改。

3.props 是一个从内部传进组件的参数,次要作用就是从父组件向子组件传递数据;state 的次要作用是用于组件保留 / 管制以及批改本人的状态,只能在 constructor 外面初始化。

三、props 和 state 的使用场景

在下面咱们理解到了 props 和 state 的基本概念和两者之间的区别,接下来咱们来理解一下 props 和 state 到底该用在哪些地方?

哪些属性应该用 state:state 应该包含那些可能被组件的事件处理器扭转并触发用户界面更新的数据,个别很小且能被 JSON 序列化。当创立一个有状态组件时,应该保持数据的精简,将起码的数据存入 this.state,在 render()里再依据 state 来计算须要的其它数据。

哪些属性不应该应用 state

(1)计算所得的数据。计算数据应该在 render()中实现,如果存储在 state 中须要手动更新 state 比拟麻烦;

(2)基于 props 的反复数据。组件中应该放弃 props 为惟一数据起源,除非须要晓得历史数据是什么;

(3)不要将 React 组件保留在 state 中。在 render()里应用 props、state 来创立它。

四、setState 要留神的点

集体认为 setState 函数最引人注意的一点就是 该函数是异步的 。也就是说, 不是执行完 setState 函数之后 this.state 就会被更新,而是期待 state 的批次更新,在开始从新渲染之前,React 会无意的进行“期待”,直到所有的组件的事件处理函数内调用的 setState()实现。

让咱们先来看一下如果间断应用屡次 setState 函数的调用的后果是怎么样的

// 不会像预期那样运行的示例
incrementCount() {
  // 留神:这样 * 不会 * 像预期的那样工作。this.setState({count: this.state.count + 1});
}

handleSomething() {
  // 假如 `this.state.count` 从 0 开始。this.incrementCount();
  this.incrementCount();
  this.incrementCount();
  // 当 React 从新渲染该组件时,`this.state.count` 会变为 1,而不是你冀望的 3。// 这是因为下面的 `incrementCount()` 函数是从 `this.state.count` 中读取数据的,// 然而 React 不会更新 `this.state.count`,直到该组件被从新渲染。// 所以最终 `incrementCount()` 每次读取 `this.state.count` 的值都是 0,并将它设为 1。// 问题的修复参见上面的阐明。}

为什么会是这样的呢?不应该执行了三次 setState()组件的 state 就会更新三次吗?

其实不是的,因为调用 setState 是异步的,可能第二个 this.setState()没等到第一次的 state 更新结束就开始执行,第三个也是如此,所以最初的 count 只加了 1。不要指望调用 setState 之后,this.state 会立刻映射为新的值。

那又该如何解决这个问题呢?怎么样能力达到咱们预期的成果呢?

在这里,若给 setState 传递一个函数,而不是一个对象,就能够确保每次的调用都是用最新的 state,参见上面的例子(援用官网事例):

办法一:

// 修复后的示例
incrementCount() {this.setState((state) => {
    // 重要:在更新的时候读取 `state`,而不是 `this.state`。return {count: state.count + 1}
  });
}

handleSomething() {
  // 假如 `this.state.count` 从 0 开始。this.incrementCount();
  this.incrementCount();
  this.incrementCount();

  // 如果你当初在这里读取 `this.state.count`,它还是会为 0。// 然而,当 React 从新渲染该组件时,它会变为 3。}

读到这里,有的同学可能会问:为什么给 setState 传递一个函数的时候 this.state 就会及时进行更新?

其实 setState 能够接管两个参数,一个参数是对象,另外一个参数是函数。而传入的函数被称为状态计算函数,构造为:

// 状态计算函数
function(state, props) => newState

其实这个函数会将每次更新退出队列中,执行时通过以后的 state 和 props 来获取新的 state,而每次更新时都会提取以后的 state,进行运算失去新的 state,就保障了数据的同步更新。

还有另外一种方法能够实现这个成果,就是同时向 setState 外面传入对象和 callback,这个 callback 是等到 this.state 更新结束之后才会调用:

办法二(不举荐):

this.setState({count: this.state.count + 1}, () => {
  this.setState({count: this.state.count + 1});
});

通过我的测试,这个办法更新两次 state,同时渲染两次组件。而办法一(官网的办法)只渲染了一次组件。

//state 更新组件只渲染了一次
handleSubmit() {
    // this.setState({
    //   count: this.state.count + 1
    // }, () => {
    //   this.setState({
    //     count: this.state.count + 1
    //   });
    // });

    this.incrementCount();
    this.incrementCount();
    this.incrementCount();

    console.log("handle" + this.state.count);
  }

  incrementCount() {this.setState((state) => {
      // 重要:在更新的时候读取 `state`,而不是 `this.state`。return {count: state.count + 1}
    });
  }

  render() {console.log("render" + this.state.count);
    return (<button onClick={this.handleSubmit}> 点击 </button>
    );
  }

运行截图:

//state 每更新一次,组件就渲染一次
handleSubmit() {
    this.setState({count: this.state.count + 1}, () => {
      this.setState({count: this.state.count + 1});
    });

    // this.incrementCount();
    // this.incrementCount();
    // this.incrementCount();

    console.log("handle" + this.state.count);
  }

  // incrementCount() {//   this.setState((state) => {
  //     // 重要:在更新的时候读取 `state`,而不是 `this.state`。//     return {count: state.count + 1}
  //   });
  // }

  render() {console.log("render" + this.state.count);
    return (<button onClick={this.handleSubmit}> 点击 </button>
    );
  }

运行截图:

所以,处于性能方面的思考,咱们更加偏向于采纳办法一,直到所有在组件的事件处理函数内调用的 setState()实现之后,才去进行 state 的更新和组件的渲染,这样能够防止不必要的从新渲染来晋升性能。

退出移动版