二十二、React.Component用法
组件(Components)
允许您将UI拆分为独立的可重用的部分,并单独的考虑每个部分。
总览@H_301_7@
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
使用方法@H_301_7@
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
中读取。
类属性@H_301_7@
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!');
实例属性@H_301_7@
props
this.props
包含由此组件的调用者定义的props
。
特别地,this.props.children
是一个特殊的props,通常由JSX表达式中的子标签而不是标签本身定义。
state
state包含特定于此组件的数据,可能随时间更改。 state是用户定义的,它应该是纯JavaScript对象。
如果你不在render()
中使用它,它不应该设置state。 例如,您可以将定时器ID直接放在实例上。
永远不要直接改变this.state
,因为调用setState()
之后可以替换你所做的各种变化, 通常应该把this.state
看作是不可变的。