关于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的更新和组件的渲染,这样能够防止不必要的从新渲染来晋升性能。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理