React组件

Components用来把UI拆分成独立的,可重复使用的部分,然后做不同的考量。React.ComponentReact提供。

概述

React.Component是一个抽象的基类,没有直接有意义的参考。通常用来定义有一个render方法的子类。

一个简洁的组件:

class Greeting extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}

如果没有使用ES6,可以使用create-react-class模块来代替。

组件生命周期

每一个组件都有一些生命周期方法,可以重写这些方法来实现在特定时间点的处理。有 will 前缀的方法是在某事发生前,有 did 前缀的方法是在某事发生后。

装载

组件实例创建并插入到DOM中时会调用这些方法:

  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount()

更新

propsstate的改变会导致更新。组件被重绘的时候会调用这些方法:

  • componentWillReceiveProps()
  • shouldComponentUpdate()
  • componentWillUpdate()
  • render()
  • componentDidUpdate()

挂载

DOM中移除组件时会调用这个方法:

  • componentWillUnmount()

其它APIs

每个组件也提供了一些其它APIs:

  • setState()
  • forceUpdate()

类属性

  • defaultProps
  • displayName

实例属性

  • props
  • state

参考

render()

render()

组件中一定要有render()方法。

被调用时会检查this.propsthis.state来返回一个React原件。这个原件也可以是一个原生DOM组件的画像,比如<div />,或者自定义的综合组件。

如果不想要渲染任何东西,可以直接返回null或者false。这时调用ReactDOM.findDOMNode(this)也会返回null

render()方法应该是纯净的,每次被调用是都应该返回相同的结果,不应该在方法里修改组件状态,或者与浏览器有直接交互。如果想要和浏览器交互,在componentDidMount()或者其它声明周期中完成你的工作。保持render()的纯净使组件更容易理解。

Note

如果shouldComponentUpdate()返回false,render()不会被执行


constructor()

constructor(props)

React组件装载之前会执行构造方法。在子类中执行构造方法时,应该在其它声明的上方执行super(props)。需要注意的是,构造方法中的this.props会变成undefined

在构造方法中初始化state很合适。如果不初始化state也不绑定方法,那不需要在组件中实现构造方法。

基于props初始化state是可以的。这些初始有效的props,可以直接设置到state中。下面是一个例子:

constructor(props) {
super(props);
this.state = {
color: props.initialColor
};
}

需要注意的是,state不会随着props及时更新。可以用lift the state up来代替同步更新propsstate

如果想要使用props设置state的值,可以使用componentWillReceiveProps(nextProps)来同步更新state。但是lift the state up更简单而且能避免错误。

lift the state up指的是把几个子类的state提升到他们最临近的父级里面,然后通过父级接收子类事件统一处理后,用props的方式再分发给子类。


componentWillMount()

componentWillMount()

componentWillMount()是在首次装载发生之前立即调用。在render()之前调用,因此在这个方法里同步设置state不会触发重绘。避免在这个方法里引入任何副作用或者订阅。

这只是一个声明周期钩子。通常推荐使用constructor()


componentDidMount()

componentDidMount()

componentDidMount()是在首次装载完成之后立即被调用。这里获取DOM元素最合适。如果有从远端请求数据的需求,可以在这里初始化网络请求。在这里设置state会触发重绘。


componentWillReceiveProps()

componentWillReceiveProps(nextProps)

componentWillReceiveProps()是在更新属性准备重绘之前调用。如果你需要根据新的props来修改state,你可以拿比较this.propsnextProps后的结果交给this.setState()来完成state的转换。

注意props没有改变的时候也会触发该方法,所以如果只是想处理改变,那请确保比较了当前值和最新值。父组件可能会导致组件重绘。

首次装载时不会调用componentWillReceiveProps。如果组件的props更新会调用这个方法。通常调用this.setState不会触发componentWillReceiveProps


shouldComponentUpdate()

shouldComponentUpdate(nextProps, nextState)

使用shouldComponentUpdate()是为了让React知道当前propsstate的更新是否影响到组件的输出。默认是每次state的更新都会重绘,多数情况下应该依靠默认行为。

shouldComponentUpdate()是在新的propsstate被接收准备重绘之前被调用。默认返回true。首次装载或使用forceUpdate()时,该方法不被调用。

如果子组件的state改变了,那即便父组件的该方法返回false,子组件依旧会重绘。

如果shouldComponentUpdate()返回false,那么componentWillUpdate()render()componentDidUpdate()都不会被调用。需要注意的是,React在未来可能会对待shouldComponentUpdate()作为一种暗示,为非一个严格的指令,返回false也会重绘组件。

如果你确定特定组件在解析时很慢,可以换用继承React.PureComponentReact.PureComponent没有实现浅比较propsstateshouldComponentUpdate()。如果你有信心自己手写,可以比较this.propsnextPropsthis.statenextState来判断更新可以跳过。

对于复杂的数据的比较是非常耗时的,而且可能无法比较,通过使用Immutable.js能够很好地解决这个问题,Immutable.js的基本原则是对于不变的对象返回相同的引用,而对于变化的对象,返回新的引用。


componentWillUpdate()

componentWillUpdate(nextProps, nextState)

componentWillUpdate()是在更新propsstate准备重绘之前被调用。这是在发生更新之前做准备的时机。首次装载的时候没有调用这个方法。

注意不能在这里调用this.setState(),应该使用componentWillReceiveProps()来根据props更新state

Note

如果shouldComponentUpdate()返回false,那么componentWillUpdate()不会被调用


componentDidUpdate()

componentDidUpdate(prevProps, prevState)

componentDidUpdate()是在重绘之后执行,首次装载时不会触发。

可以在这个时机操作组件更新完成后的DOM。可以比较props的改变在这里做网络请求。(props如果没有改变,可能不需要发起网络请求)。

Note

如果shouldComponentUpdate()返回false,那么componentDidUpdate()不会被调用


componentWillUnmount()

componentWillUnmount()

componentWillUnmount()在组件被卸载和销毁之前会被调用。在这个方法里执行必要的清理工作,比如没用的计时器,取消网络请求,或者是清除在componentDidMount执行时创建的DOM元素。


setState()

setState(updater, [callback])

setState()更新组件state,并且告诉React这个组件需要根据state的更新来重绘,但只是加入到执行队列。这是一个更新用户操作和处理服务端数据的主要方法。

请把setState()当做一个请求而非立即执行方法。在单线程执行多个组件时,给了更好的性能,React可能会延迟执行它。React不能保证state更新被立即执行。

setState()不总是立即更新组件。它会把JavaScript执行进度中调用的setState()合并在一起,延迟执行。在setState()之后读取this.state是有问题的。用来代替的是,使用componentDidUpdate或者一个setState回调(setState(updater, callback)),或者是更新完成后保证可以被触发的方法。如果你需要前一个state来设置当前state,请看下面的updater

setState()总是会触发重绘,除非shouldComponentUpdate()返回false。如果由于对象很复杂没办法使用shouldComponentUpdate()来判断是否重绘,可以执行setState()在新的state与原有state存在变更的时候来避免不必要的重绘。

第一个参数可以是像这样的updater方法:

(prevState, props) => nextState

prevState是对之前state的引用。不应该直接改变。应该通过根据prevStateprops的输入构建一个新的状态对象来表示更改。例如,假设我们想根据props.step增加一个变量值:

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

updater收到的prevStateprops都是最新的。

setState()第二个参数是一个可选的回调方法,一旦setState执行完并且组件重绘完时执行。通常推荐使用componentDidUpdate()

也可以通过第一个参数为对象给setState()代替方法:

setState(stateChange, [callback])

会执行一个浅复制stateChange到新的state,例如调整购物车商品数量:

this.setState({quantity: 2})

这种格式的setState()也是异步的,并且同一个生命周期的多次请求会合并在一起。比如想在同一个生命周期加入增加数量,结果相当于:

Object.assign(
previousState,
{quantity: state.quantity + 1},
{quantity: state.quantity + 1},
...
)

在同一生命周期中后续请求会覆盖之前请求,所以数量只会增加一次。如果后面的state依赖前面的state,推荐使用updater方法代替:

this.setState((prevState) => {
return {counter: prevState.quantity + 1};
});

forceUpdate()

component.forceUpdate(callback)

默认,如果组件里面的state或者props更新,组件会重绘。但是如果render()调用依赖其它的数据,可以通过forceUpdate()来触发组件渲染。

调用forceUpdate()会导致组件render()执行,并且跳过shouldComponentUpdate()。这将触发子组件的正常生命周期方法,包括每个子组件的shouldComponentUpdate()。如果标记更新,React会仅仅修改DOM

通常应该避免使用forceUpdate()并且只使用this.propsthis.state来读取。


类属性

defaultProps

defaultProps可以用来定义组件自己的默认属性,设置类的默认props。这是使用于未声明的props,但不使用于空props。例如:

class CustomButton extends React.Component {
// ...
}

CustomButton.defaultProps = {
color: 'blue'
};

如果props.color没有被提供,它会被默认设置为'blue'

render() {
return <CustomButton /> ; // props.color会被设置为blue
}

如果props.color被设置为null,那它就是null

render() {
return <CustomButton color={null} /> ; // props.color会被设置为null
}

displayName

displayName被用作debug信息。


实例属性

props

this.props包含组件调用者定义的props。尤其是,this.props.children是一个特殊的属性,通常由JSX表达式中的子标签定义,而不是标记本身。

state

state包含特定于该组件的状态,随时间而改变。state是用户自定义的,应该是一个清晰的JavaScript对象。

不要直接改变this.state,因为setState()可能会替换你的改变。把this.state当做不可变的。