首先在这里引用 react 官网的几句话
React :元素构成组件,组件又构成应用。
React核心思想是组件化,其中 组件 通过属性(props) 和 状态(state)传递数据。
State 与 Props 区别
引用一句很精髓的概括:props 是组件对外的接口,state 是组件对内的接口 。这句话其实很好的解释了 state 与 Props 的区别。
Props属性:组件对外的接口,通过 Props 进行组件之间的数据传递
State状态:除了上层组件传递的 Props数据之外,组件内部也会存在独有数据,在组件内部根据不同的情况改变,从而维护改变组件状态
props
1、props是单向传递的,只能从父组件传递到子组件,和vue的数据传递类似。
2、在子组件内部使用 父组件传递过来的 props数据时,需要使用 this.props.xxx 才能调用
3、组件内部的 props 只能读取,不能被更改。如果非要更改的话,可以让父组件传递一个事件,在子组件中调用事件的同时,将数据传递到父组件中
4、父组件向子组件中传递数据时,如果需要传递的数据很多,可以使用扩展运算符来便捷传递
井字棋案例中:
父组件 Game.js 调用了子组件 Board.js ,且传递了 当前 点击之后的单元格数据,也就是当前游戏进程。同时,也传递了一个 onClick 事件,没错,这个事件也被传递到子组件了。
<Board
squares={current.squares}
onClick={i => {
this.handleClick(i);
}}
/>
然后在子组件 Board.js 组件中 通过 this.props.xxx 获取父组件传递的数据,渲染单元格,而且,在点击单元格的时候,会触发父组件 传递的事件,也就是说,这个事件,实际上是在父组件内部触发的。
<Square
key={i}
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>
从这里就可以看到,父组件传递的数据,子组件需要使用 this.props.xxx 接收 。子组件要想改变props,可以通过 事件传递的方式。
传递多个属性,如果我们在调用子组件时,向这样 通过 props 传递多个属性,无疑是很麻烦的,所以才有了下面的简便方法
import React from 'react';
import ReactDOM from 'react-dom';
class Me extends React.Component {
render() {
return <h1>{this.props.a} - {this.props.b} - {this.props.c}</h1>
}
}
ReactDOM.render(
// 向子组件内部 传递了多个数据,这样写会很麻烦
<Me a="1" b="2" c="3" />,
document.getElementById('root')
);
通过对象传递:通过 Props 传递一个对象给子组件,子组件也需要接收一个对象,从这个对象内部取值
import React from 'react';
import ReactDOM from 'react-dom';
class Me extends React.Component {
render() {
return <h1>{this.props.data.a} - {this.props.data.b} - {this.props.data.c}</h1>
}
}
const sj = {
a: '1',
b: '2',
c: '3'
};
ReactDOM.render(
<Me data={sj}/>,
document.getElementById('root')
);
通过扩展运算符传递一个对象:子组件接收的时候,可以直接使用 this.props.xxx 获取属性值
import React from 'react';
import ReactDOM from 'react-dom';
class Me extends React.Component {
render() {
return <h1>{this.props.a} - {this.props.b} - {this.props.c}</h1>
}
}
const sj = {
a: '1',
b: '2',
c: '3'
};
ReactDOM.render(
// 通过扩展运算符传递一个对象
<Me {...sj} />,
document.getElementById('root')
);
总结:显然通过扩展运算符更方便,因为它即将数据包装成了一个我们常用的对象格式,而且还采用扩展运算符简化了赋值的书写
State
官网中是这么说的:State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。 State是一个组件的UI数据模型,是组件渲染时的数据依据。
正确地使用 State
1、构造函数是唯一可以给 this.state
赋值的地方:
constructor(props) {
super(props);
this.state = {
history: [{ squares: Array(9).fill(null) }],
xIsNext: true,
setNumber: 0,
coordinate: [[0, 0]], //初始位置默认为坐标[0,0]
isReverse: false
};
}
2、通过 setState() 来修改 state
直接修改 state 内部的属性,是不会生效的,这样并不会重新渲染组件
this.state.setNumber = 1111
而是需要通过 setState() 来修改数据
this.setState({
setNumber : 1111
})
3、State 的更新是异步的
当我的数据更新之后,我的视图层是不会立即更新的,而是会进入一个队列中,等到下次这个队列执行的时候在开始更新Dom,和vue类似。类似与vue的 nexttick
- 调用setState后,setState会把要修改的状态放入一个队列中(因而 组件的state并不会立即改变)
- State 的更新会被合并。调用 setState 多次修改同一个属性值的时候,会依次添加到队列中,且最后一次添加的会覆盖之前
- 子组件接收到的 Props 可能是父组件 中的 State ,所以在父组件中改变 State之后,子组件中的 Props 数据,可能是不会立即更新的,所以 我们不能依靠 State 来去计算下一个组件的值
总结:this.props 和 this.state 可能是异步更新的,不能依赖他们的值计算下一个state(状态)
异步更新State
State的更新是异步的,和vue类似,但是vue中有 nexttick 可以触发更新,那么react中同样也可以异步的去更新Dom,这个同样还是需要用到 setState() 这个方法。
这个是官网的错误的例子,是一个计算器,数据相加之后,是不会立即更新当前值的
this.setState({
counter: this.state.counter + this.props.increment,
});
要解决这个问题,可以让 setState()
接收一个函数 而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
//注意:下面这样是错的
this.setState((state, props) => { //没将{}用()括起来,所以会解析成代码块
counter: state.counter + props.increment
});
当State变化,创建新的State
到了这一步,其实就比较好理解了,我们的数据如果是简单类型数据的话,直接赋值改变就可以,但是如果 是引用数据类型的话,就需要处理一下,我们需要创建一个副本,然后去操作这个副本,最后把处理过后的副本的值,重新通过 setState 去改变 State
基本类型:直接赋值
//原state
this.state = {
count: 0,
title : 'React',
success:false
}
//改变state
this.setState({
count: 1,
title: 'bty',
success: true
})
引用类型:数组,此时不能直接赋值,而是需要得到一个新的数组副本,然后 通过 setState 重新赋值
在井字棋案例中,频繁使用了 副本数据,可以直接 先得到一个副本,然后通过数组方法 concat,splice、map,等处理数组之后返回一个新数组的方法
const history = this.state.history.slice();
let history = this.state.history;
注意:不要使用push、pop、shift、unshift、splice等方法修改数组类型的状态,因为这些方法都是在原数组的基础上修改,而concat、slice、filter会返回一个新的数组。
创建新的状态对象的关键是,避免使用会直接修改原对象的方法,而是使用可以返回一个新对象的方法。
数据是向下流动的
每个组件的 State 数据,都可以当作 Props 中的数据,传递到子组件,而每个子组件内部,都可能存在自己私有的 State 数据,在子组件中,并不关心我得到的 Props 中的数据,是上级组件的 State 中的数据,还是Props数据(也就是更上一级的State数据)