React-prop & state & this.setState()-4

React组件的数据分两种, propsstate 。并且【数据是自顶向下单向流动】的,即父组件到子组件。 无论 props 和 state 的改变,都有可能引发组件的重新渲染。 把组件看成一个函数, 那么它接受了 props 作为参数, 内部由 state 作为函数的内部参数,返回一个 Virtual DOM。(也就是说, prop 是组件的对外接口, state 是组件的内部状态,对外用 prop, 对内用 state)

1、props

· 在 React中, props 本身是不可变的。组件 props 一定来是自于默认属性或通过组件传递而来的数据
· 组件通过定义自己能够接受的 prop 就定义了自己的对外公共接口
· 如果顶层组件初始化 props ,那么 React 会向下遍历整棵组件树, 重新尝试渲染所有相关的子组件
· 每个 React 组件都是独立存在的模块, 组件外的一切就是外部世界, 外部世界就是通过 prop 来和组件对话的

给 prop 赋值

· 从外部世界传值,组件的 prop 赋值的类型可以是任何 JavaScript 语言支持的数据类型

<Button borderWidth={ 2 } onClick={ this.onBtnClick } style={{ color: 'red' }}/>

父子组件通过 prop 对话

组件之间通信,子组件不能直接修改父组件传递过来的数据, 因为 props 是一个只读的属性,如果需要修改父组件传递的数据, 子组件只能通过父组件传递来的事件函数来修改( 子组件接受的props就是 父组件里的(state )

// 父组件
class Panel extends Component {
  constructor( props ){
   super( props );
     this.state = { count: 1 }
     this.parentClick = this.parentClick.bind( this );
  }
  render() {
    return (
	   <Counter count={ this.state.count } parentClick={ this.parentClick } />
    );
  }
  parentClick(){
    this.setState( () => {
      count: this.state.count +1
    } )
  }
};

// 子组件
class Counter extends Component {
  constructor( props ){
    super( props );
    this.ChangeParentState = this.ChangeParentState.bind( this );
  }
  ChangeParentState(){
    const { count, parentClick } = this.props;
    parentClick( count );
  }
};

注意: prop 传递函数时有以下几种方式

1)行间绑定 this 传递参数, Event对象永远在函数参数的最后一个: parentClick( …args, ev )

<Counter parentClick={ this.parentClick.bind( this, args ) } />

2)利用箭头函数传递参数

constructor( props ){
  super( props );
  this.parentClick = this.parentClick.bind( this );
};
<Counter count={ this.state.count } parentClick={ ()=> { this.parentClick( args ) } } />

3)读取 prop 值:

· 如果组件要定义自己的构造函数, 一定要通过 super() 调用父类 React.Component 的构造函数
· 如果在构造函数中没有调用 super( props ); 那么组件实例化后, 类实例的所有成员函数无法通过this.props 访问父组件传递过来的 props 值

class Counter extends Component {
  constructor( props ){
    super( props );
    this.state = {
      count: props.count;
    }
  }
  render(){
    const { count } = this.props;
  }
};

PropTypes类型检查

· 1、propTypes 属性来定义 prop 规格
· 2、import PropTypes from ‘prop-types’;
· 3、isRequired: 必填项

class Counter extends Component {
  constructor( props ){
    super( props );
    this.state = { count: '1', initValue: 2, index: 'hello' }
  }
  handleClick(){}
};
	
Counter.propTypes = {
  count: PropTypes.string.isRequired,
  initValue: PropTypes.number,
  handleClick: PropTypes.func,
  index: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number
  ])
};

defaultProps 默认属性值

class Panel extends Component {
  constructor( props ){
    super( props );
    this.state = { count: '', initValue: '', index: 'hello' }
  }
  handleClick(){}
};
Panel.defaultProps = {
  count: '1',
  initValue: 2
};

3、state

1)由于 React 组件不能修改传入的 prop, 所以需要记录自身数据变化, 就要使用 state。通常在组件类的构造函数结尾处初始化( constructor()中 ) state

2)state 代表组件的内部状态, 通过 this.state 可以读取到组件的 state。组件的 state 必须要使用 this.setState() 修改, 而不能直接修改 this.state。直接修改 this.state 的值,虽然改变了组件的内部状态 state, 却没有驱动组件进行重新渲染, 导致视图层不会反应 this.state 值的变化

3)this.setState() 做的事情, 首先改变 this.state 的值,然后驱动组件经历更新过程,该组件会重新渲染,这样才能让 this.state 里新的值出现在界面上

4)组件的 state 必须是一个 JavaScript 对象, 不能是 string 或者 number 这样简单的数据类型, 即使存储的是一个 number 类型的数据, 只能把它存作 state 某个字段对应的值

初始化 state

class List extends Component {
  constructor( props ){
    super( props );
    this.state = { count: '' };
  }
};

读取和更新 state

class List extends Component {
  constructor( props ){
    super( props );
    this.state = { count: '' };
  }
  handleChangeValue(){
    this.setState( () => ( { count: this.state.count + 1 } ) );
  }
};

4、 prop 和 state 的对比

1) 总结 prop 和 state 的区别:

· 1、prop 用于定义外部接口, state 用于记录内部状态
· 2、prop 的赋值是在外部世界使用组件时, state 的赋值在组件内部
· 3、组件不应该改变 prop 的值, 而 state 存在的目的就是让组件来改变的

5、关于 this.setState() 更新的问题

1)this.setState() 异步更新

this.setState()通过一个队列机制实现 state 更新。当执行 this.setState() 时,会将需要更新的 state 合并后放入状态队列, 而不会立刻更新 this.state, 队列机制可以高效的批量更新 state。如果不通过 this.setState() 而直接修改 this.state 的值,那么该 this.state 将不会放入状态队列中, 当下次调用 this.setState() 并对状态队列进行合并时, 将会忽略之前直接修改的 this.state 的值, 导致视图无法更新。同时 React 也正是利用状态队列机制实现了this.setState() 的异步更新, 避免频繁重复的更新 state。

2)this.setState() 的中回调函数

因为 this.props 和 this.state 可能是异步更新的, 这样会导致数据取不到的情况。而使用 this.setState() 来接受一个函数而不是一个对象就可以解决这个问题.该函数将接受先前的状态( prevState )作为一个参数, 此次更新被应用的 props 作为第二个参数; prevState是之前状态的引用(上一次的数据状态),而且不应该被直接改变

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

3)this.setState() 中的第二个回调函数

因为 this.setState() 是异步更新的, 所以在一些场景中需要使用更新过的数据,那么可以使用 this.setState() 中的第二个回调函数

this.setState(
  ( prevState, props ) => ( {
    counter: prevState.counter + props.step
  } ),
  () => {
    console.log( this.state.counter );
  }
);

props, state 与 render()函数之间的关系

· 当组件的 state 或者 props 发生改变的时候, render() 函数就会重新执行
· 当父组件的 render() 函数被运行时, 它的子组件的 render() 函数都将被重新执行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值