二十二、React.Component用法
组件(Components)
允许您将UI拆分为独立的可重用的部分,并单独的考虑每个部分。
总览
React.Component
是一个抽象基类。这意味着直接引用React.Component
是毫无意义的。你可以实现一个它的子类,并且至少定义一个render()
方法即可使用。
你可以使用ES6中class
定义一个React组件:
class Greeting extends React.Component { render() { return <h1>Hello,{this.props.name}</hr>; } }
如果你还没有使用ES6,你可以使用React.createClass()
。
组件的生命周期方法
每个组件都有几个『生命周期方法』,您可以重写这些方法,已便在React执行过程中的指定时间运行自己的代码。前缀为Will
的生命周期方法会在在一些事情发生之前被调用,带有Did
前缀的方法在某些事情发生之后被调用。
Mounting(加载组件)
constructor()
componentWillMount()
render()
componentDidMount()
Updating(更新状态)
更新可以由prop或者state的改变引起。在重新渲染组件时依次调用这些方法:
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
render()
componentDidUpdate()
Unmounting(卸载组件)
componentWillUnmount()
其他API
每个组件还提供了一些其他API:
setState()
forceUpdate()
组件属性
defaultProps
displayName
propTypes
实例内部属性
props
state
使用方法
render()
render() { // return React Elements }
注意:render()
方法是必须写的。
当这个方法被调用时,它会检测this.props
和this.state
并返回一个React元素。此元素可以是本地DOM组件的形式,例如<div/>
,也可以是您自己定义的一个复合组件。
你也可以返回null
或false
来表示你不想做任何渲染操作。当返回null
或false
时,ReactDOM.findDOMNode(this)
将返回null
。
render()
方法应该是纯的(pure function
,见函数式编程),这意味着它并不会修改组件state,每次调用它时都会返回相同的结果,它不会直接与浏览器交互。如果您需要与浏览器直接交互,请改用componentDidMount()
方法或者其他生命周期方法来执行你的逻辑。保持render()
的纯
可以让组件更容易去思考自己应该做什么。
提示
如果shouldComponentUpdate()
返回false
,那么render()
不会被执行。
constructor()
constructor(props)
在装载组件(mounting)之前调用会React组件的构造函数。当实现React.Component
子类的构造函数时,应该在任何其他语句之前调用super(props)
。否则,this.props
将在构造函数中未定义,这可能导致错误。
构造函数是初始化state的标准位置。如果不初始化state,并且不绑定组件内部方法的this指向,则不需要为React组件实现构造函数。
如果你知道你在做什么的话,你可以根据props来初始化state。这里有一个有效的React.Component
子类构造函数的例子:
constructor(props) { super(props); this.state = { color: props.initialColor }; }
注意这种模式,因为它会将props复制一份在state中,这就可能导致一个意外的bug。所以不应该将props复制到state中。相反,你需要使用提升state
的技巧,该技巧我们在前面的文章提到过。
如果你使用props复制到state中,你还需要实现componentWillReceiveProps(nextProps)
来保持state是最新的。这个时候使用提升state的方法反而会更容易,也能产生更少的bug。
componentWillMount()
componentWillMount()
componentWillMount()
是在装载(mounting)发生之前被调用。它在render()
之前调用,所以在此方法中的设置state不会造成重新渲染。另外,应该避免在此方法中引入有任何副作用的东西(见函数式编程)
。
在服务器渲染上这是唯一一个调用的生命周期钩子函数。一般来说,我们建议使用constructor()
。
componentDidMount()
componentDidMount()
componentDidMount()
在组件装载到DOM后立即调用。如果需要进行DOM节点的初始化则应该在这里来执行该逻辑。如果需要从远程端点加载数据(ajax
),那么这是处理网络请求的最好地方。在此方法中设置state会去重新渲染DOM。
componentWillReceiveProps()
componentWillReceiveProps(nextProps)
componentWillReceiveProps()
在安装好的组件接收新props之前被调用。 如果你需要更新state用来响应props的更改(例如,重置它),你可以在此方法中比较this.props
和nextProps
并使用this.setState()
来替换并重置state。
注意,即使props没有改变,React
也可以调用这个方法,因此如果你只想处理props改变的情况,请确保比较当前值和下一个值是否不同。 当父组件引起你的组件重新渲染时,就有可能会发生这种情况。
如果你只是调用this.setState()
,那么componentWillReceiveProps()
不会被调用。
shouldComponentUpdate()
shouldComponentUpdate(nextProps,netState)
使用shouldComponentUpdate()
让React知道组件是否受当前state或props变化的影响。 默认行为是在每次state更改时都会去重新渲染DOM,在绝大多数情况下,你应该依赖于这个默认行为。
当接收到新的props或state时,shouldComponentUpdate()
在渲染之前被调用。 默认为true
对于初始渲染或使用forceUpdate()
,不调用此方法。
返回false
不会阻止子组件在state更改时重新渲染。
目前,如果shouldComponentUpdate()
返回false
,那么将不会调用componentWillUpdate()
,render()
和componentDidUpdate()
。 注意,在将来React可以将shouldComponentUpdate()
作为提示而不是严格的操作指令
,返回false
仍然可能导致组件的重新渲染。
如果你确定某些组件在某些操作时有点缓慢,你可以让它继承React.PureComponent
,而不是继承React.Component
。
React.PureComponent
实现了props
和state
进行浅比较的shouldComponentUpdate()
方法。 如果你确定想人肉处理这个浅比较
操作,你可以自己在这个函数中比较this.props
和nextProps
、this.state
和nextState
是否相同。相同返回false
,不同返回true
,那么React就会根据返回值来确认是否跳过本次DOM渲染。
componentWilUpdate()
componentWillUpdate(nextProps,nextState)
当组件在收到新的props或state时,componentWillUpdate()
在渲染之前会立即调用这个方法。 使用这个方法来判断是非需要重新渲染DOM。 第一次渲染DOM不调用此方法。
注意,this.setState()
不会调用此方法。 如果你需要根据state和props来进行重新渲染DOM,请改用componentWillReceiveProps()
。
note
如果shouldComponentUpdate()
返回false
,则不会调用componentWillUpdate()
。
componentDidUpdate()
componentDidUpdate(prevProps,prevState)
componentDidUpdate()
在重新渲染DOM之后被调用。 第一次渲染不调用此方法。
当组件已经重新渲染后,此方法是一个执行DOM操作的好机会,同时也是一个处理网络请求的好地方,前提是你需要比较当前props与之前的props是否相同(例如,如果props没有改变,那么可能不需要进行网络请求)。
note
如果shouldComponentUpdate()
返回false
,则不会调用componentDidUpdate()
。
componentWillUnmount()
componentWillUnmount()
componentWillUnmount()
在组件被卸载和销毁之前立即被调用。 此方法可以执行任何有必要的清理工作,例如清理计时器,取消网络请求或清理在componentDidMount()
中创建的DOM元素。
setState()
setState(nextState,callback)
将nextState和当前state进行浅合并。 这是用于从事件处理函数和服务器请求回调中触发UI重新渲染的主要方法。
第一个参数可以是一个对象(包含一个或多个要更新的state属性),也可以是返回将要引起重新渲染的对象(state和props)。
这里有一个简单的用法:
this.setState({myKey: 'my new value'});
它也可以传入一个带有参数的函数function(state,props)=> newState
。 例如,假设我们想要当前state中的myInteger加上props.step:
this.setState(prevState,props) => { return {myInteger: prevState.myInteger + props.step}; }
第二种参数是回调函数,一旦setState完成并且组件被重新渲染,它就会被执行。 通常我们建议使用componentDidUpdate()
代替这样的逻辑。
setState()
不会立即改变this.state
,但会创建并挂起state的修改。 所以在调用此方法中访问this.state
可能会返回现有值。
不能保证对setState
的所以调用都是同步操作,因为这样做是为了将多次state修改合并为一次以便提高性能。
setState()
总会重新渲染DOM,除非shouldComponentUpdate()
回false。 如果你正在使用可突变对象,并且无法在shouldComponentUpdate()
实现条件渲染逻辑,则只有当新state与先前state不同时调用setState()
才能避免不必要的重新渲染。
forceUpdate()
component.forceUpdate(callback)
默认情况下,当组件的state或props改变时,组件将重新渲染。 如果你的render()
方法依赖于一些其他数据,你可以告诉React组件需要通过调用forceUpdate()
来重新渲染。
调用forceUpdate()
会导致在组件上调用render()
,跳过shouldComponentUpdate()
这将触发子组件的正常生命周期方法,包括每个子组件的shouldComponentUpdate()
方法。 如果标记更改,React仍将更新DOM。
通常你应该尽量避免forceUpdate()
的所有使用,并且只能从render()
中的this.props
和this.state
中读取。
类属性
defaultProps
defaultProps
是类组件本身的属性,用来设置类组件的默认props。 可以用来给未传入值的props设置默认值。 例如:
class CustomButton extends React.Component { // ... } CustomButton.defaultProps = { color: 'blue'; }
如果props.color
没有定义,就是将它设置为默认值blue
:
render() { return <CustomButton />; // props.color will be set to blue }
如果props.color
被设置为null
,那么它将会被重新赋值为null:
render() { return <CustomButton color={null} />; // props.color will remain null }
displayName
displayName
字符串用于调试消息。 JSX
自动设置此值;
propTypes
propTypes
也是类组件本身上的一个属性,用来规范props应该是什么类型。 它是从props的名称到React.PropTypes
中定义的类型的映射。 在开发模式下,当为prop设置一个不是指定格式的无效值时,会在JavaScript控制台中显示警告信息。 在生产模式下,为了提高效率,不会进行propTypes
检查。
例如,此代码确保颜色prop是一个字符串:
class CustomButton extends React.Component { // ... } CustomButton.propTypes = { color: React.PropTypes.string };
我们建议尽可能使用Flow
,以便在编译时进行类型检查,而不是在运行时进行类型检查。 Flow
在React中内置支持,因此可以轻松地在React应用程序上运行静态分析。
// @flow function foo(x) { return x * 10; } foo('Hello,world!'); // @flow function bar(x): string { return x.length; } bar('Hello,world!');
实例属性
props
this.props
包含由此组件的调用者定义的props
。
特别地,this.props.children
是一个特殊的props,通常由JSX表达式中的子标签而不是标签本身定义。
state
state包含特定于此组件的数据,可能随时间更改。 state是用户定义的,它应该是纯JavaScript对象。
如果你不在render()
中使用它,它不应该设置state。 例如,您可以将定时器ID直接放在实例上。
永远不要直接改变this.state
,因为调用setState()
之后可以替换你所做的各种变化, 通常应该把this.state
看作是不可变的。