React.Component 文档的再读

0、生命周期大图

render()constructor() 在 react 声明周期中的位置:

11.png

一、render()

React.Component 中明确指出 render() 方法是在使用 class(es6) 创建组件时必须实现的方法。

如果一个类组件中没有 render() 方法,会直接抛出错误。

render() 方法中,很多时候回去操作 this.props 或者 this.state 然后在返回内容,而返回内容的类型有多种.

1、返回 React 元素

这里的 React 元素实际上包含多种,比如通过 JSX 创建的组件,或者是原生的 DOM 组件,而在 react Native 中还可能返回 native 组件,基本上是能够被渲染的或者是其他用户创建的组件,归根结底的目的就是 render,在多么复杂封装的组件,也是为了 render。

2、返回 Arrays 和 fragments

fragment 是一个非常实用的东西,尤其是需要考虑 DOM 层级的时候。

在 React 中,具体的表现是 React.Fragment ,在组件的 render 中使用 React.Fragment 在很多时候也是必须要做的事情,比如要返回某个 table 中的某个 tr 的 几个 td,虽然这种情况比较少,但很适合做例子:

3、返回 portals

之前已经提到了 Portal 的用处,可以在http://www.ptbird.cn/react-portal-createPortal.html 了解,React.createPortal 对于需要讲子元素挂到父组件外的时候,而且需要事件冒泡,非常有用。

4、返回 String 和 Numbers

如果返回 String 或者是 Number 会直接渲染成 text 节点。

5、返回 Boolean 或者是 null

Bollean 类型和 null 类型不会进行任何渲染,一般使用的时候都是 isNumber ? <Component/> : null,在 rax 中,是必须返回 null,否则会渲染一个空的节点。

render() 方法,官方的建议应该是一个纯函数,并且不应该在 render() 中改变 state,如果外接的参数或者状态没发生变化,每次返回的值都应该是一样的。而且 render() 方法不应该与浏览器直接进行交互。

一般进行浏览器的组件交互是通过状态变动完成,比如点击或者文本输入等,而 state 的变动不应该在 render 中进行,而是应该在 componentDidMount 或者其他的生命周期方法或者是其他的事件处理监听中实现,一个纯函数的 render 方法,组件更加容易理解。

4、注意

如果 shouldComponentUpdate 中返回 false, render 方法是不会变动的(文档中说的 will not be invoked

二、constructor

1、说明

constructor 仅在创建过程中作用,并且如果不绑定方法也不初始化 state,就没必要去实现 constructor

React 组件的构造函数在 mounted 之前被调用,一般来说在 React.Component 的子类中去重写,并且应该在其他的语句之前调用 super(props)。否则,在 constructor 中可能会存在 this.props 未实现问题。

一般在 React 组件的 constructor 中只做两件事情:

  • 通过 this.state 初始化 state
  • 对事件方法进行 bind

constructor不能调用 setState,如果需要在 constructor 中给 state 赋值,直接使用 this.state = {} 就行了。

constructor(props) {
  super(props);
  // Don't call this.setState() here!
  this.state = { counter: 0 };
  this.handleClick = this.handleClick.bind(this);
}

文档中多次提到,this.state 只有在 constructor 中才能直接进行分配,其他任何方法或者声明周期中,都应该调用 this.setState 进行主动设置。

应当避免在 constructor 中使用任何可能造成副作用的操作,一般来说在 componentDidMount 中进行这些操作更加合适。

2、注意事项

避免直接将 this.props 复制到 this.state

虽然我也经常这样做

比如:

constructor(props) {
 super(props);
 // Don't do this!
 this.state = { color: props.color };
}

不过官方文档中主要指出的是两个点,一个是可以直接使用 this.props.xxx 以及 this.props.xxx 的更新并不会带动 this.state 的更新。

但是我觉得这两个理由其实没什么说服力,因为一般来说把 this.props.xxx 直接赋值给 this.state 的情况打大部分(至少我是)都是充当一个 default value 的角色,然后这个组件的状态不再随着 this.props.xxs 的更新而更新,其更新可能是有自己完成的。

比如我有一个组件是 <Parent ref='parent' /> 还有一个组件是 <Child ref='child' />,在 Child 中有一个方法是 updateState()

class Child extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            color: this.props.color
        }
    }
    updateState = (data, callback = () => {}) => {
        this.setState(data, callback);
    }
    render() {
        return <View ref={'child'} />
    }
    
}

一开始的时候我在 constructor 中初始化了 state.color,不过后面我不再通过 this.props.color 去更新,因为 this.state.color 的变化可能与 Parent 无关了。

updateState(data) 方法则是直接在 Child 中进行 state 的更新。

this.refs.parent && this.refs.parent.refs.child && this.refs.parent.child.updateState({color: newColor})

这种调用方式虽然并不是推荐的方式(会造成强耦合),但是有些情况下特别有用。

而对于派生 state React 也有一篇博客: