React 中父组件给子组件传值

2021-03-10 0 By admin

在 React 前端框架开发过程中,父组件和子组件之间相互传输数据等操作是基本的开发知识。但是在使用过程中,难免会有一些意想不到的Bug。
这里就记录了一个踩坑经历,表现的现象为父组件给子组件传输新的数据,但是数据没有在组件中生效。

一、问题说明

父组件更新子组件的props,在子组件接收到新的props时,需要更新子组件的state,但是却没有重新渲染。我想在 componentWillReceiveProps方法 中更新子组件的state,但是却没有重新渲染。
官网上有这么一句话:

在该函数中调用 this.setState() 将不会引起第二次渲染。

如果不重新渲染,那么获取到的新数据怎么更新到视图上去?

二、子组件显示父组件传过来的 props 的两种方法

2.1、直接使用

这种方式,父组件改变props后,子组件重新渲染,由于直接使用的props,所以我们不需要做什么就可以正常显示最新的props。

class Child extends Component {
    render() {
        return <div>{this.props.someThings}</div>
    }
}

2.2、子组件将 props 转化为子组件的 state

由于会将父组件传输过来的数据,转化到子组件的 state 数据中;所以需要使用 componentWillReceiveProps 生命周期函数。

子组件接受新的 props 数据,并转化到子组件的 state 数据中的过程:

  1. 子组件接受到新的 props 数据后,会重新渲染一次子组件。除非你做了处理来阻止(比如使用:shouldComponentUpdate)。
  2. 子组件设置 state 数据时,会重新渲染一次子组件。
  3. 按照上述过程,子组件会被渲染两次,这样会造成性能浪费;所以 componentWillReceiveProps 函数优化为不引起第二次渲染。
class Child extends Component {
    constructor(props) {
        super(props);
        this.state = {
            someThings: props.someThings
        };
    }
    componentWillReceiveProps(nextProps) {
        this.setState({someThings: nextProps.someThings});
    }
    render() {
        return <div>{this.state.someThings}</div>
    }
}

三、说明补充

3.1、摘录一

这里说的不会造成第二次的渲染,并不是说这里的setState不会生效。
在这个方法里调用 setState 会在组件更新完成之后在 render 方法执行之前更新状态,将两次的渲染合并在一起。
可以在 componentWillReceiveProps 执行 setState ,但是如果你想在这个方法里获取 this.state 得到的将会是上一次的状态。

3.2、摘录二

永远不要在 constructor 里使用 props 初始化state 值,不然就会导致这个问题。
要么直接用props渲染,要么使用新的生命周期函数。

class Child extends Component {
  state = { text: '' };
  //getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates.
  //It should return an object to update the state, or null to update nothing.
  //getDerivedStateFromProps在调用render方法之前被调用,无论是在初始装载还是在随后的更新中。
  //它应该返回一个对象来更新状态,或者返回null来不更新任何内容。
  static getDerivedStateFromProps(props) {
  return {
    text: props.text
  };
  }

  render() {
  return <p>{this.state.text}</p>
  }
}

class Parent extends Component {
  state = {
  name: 'xxx'
  }

  render() {
  return (
    <div>
    <Child text={this.state.name}/>
    <button onClick={() => this.setState({name: 'zzz'})}>change</button>
    </div>
  )
  }
}