react学习(6)——关于组件生命周期的问题
在项目开发的过程中,遇到了一个问题:
父组件请求后台数据,收到后将数据以props传给子组件,子组件根据收到数据的不同,显示不同的内容,同时,子组件自身可以根据click操作改变根据父组件的数据显示的内容。
因此,子组件收到父组件的props传值后,由于props值不能修改,因此子组件需要将该props值放入state,子组件根据自身click操作改变state,进而改变组件显示的内容。
而父组件传过来的值,子组件在componentDidMount中无法获取,可以在render中获取。但是若将获取值的部分写在render中,会导致页面每次更新都从父组件拿值,子组件的click操作修改值无法起作用。
子组件代码如下:
class child extends PureComponent { static propTypes = { value: PropTypes.bool.isRequired }; constructor(props) { super( props); this.state = { value: false }; } changeValue = () => { this.setState({ value: false }); } render() { this.state = { value: this.props.value }; return ( <div style={{ display: value ? 'block':'none' }}> <img src={src} alt="" /> <div className={styles.value}>这是父组件的传值:{value}</div> <div className={styles['icon-close']} onClick={this.changeValue}> <img src={close} alt="" /> </div> </div> ); } } export default Child;
解决方法1:将click的close方法写在render中
但该写法不是很规范,render() 函数应该是纯粹的,也就是说该函数不修改组件state,每次调用都返回相同的结果,不读写 DOM 信息,也不和浏览器交互(例如通过使用 setTimeout)。
class child extends PureComponent { static propTypes = { value: PropTypes.bool.isRequired }; constructor(props) { super( props); this.state = { value: false }; } render() { this.state = { value: this.props.value }; changeValue = () => { this.setState({ value: false }); } return ( <div style={{ display: value ? 'block':'none' }}> <img src={src} alt="" /> <div className={styles.value}>这是父组件的传值:{value}</div> <div className={styles['icon-close']} onClick={changeValue}> <img src={close} alt="" /> </div> </div> ); } } export default Child;
解决方法2:使用componentWillReceiveProps
class child extends PureComponent { static propTypes = { value: PropTypes.bool.isRequired }; constructor(props) { super( props); this.state = { value: false }; } componentWillReceiveProps(nextProps) { this.setState({ value: nextProps.value }); } changeValue = () => { this.setState({ value: false }); } render() { return ( <div style={{ display: value ? 'block':'none' }}> <img src={src} alt="" /> <div className={styles.value}>这是父组件的传值:{value}</div> <div className={styles['icon-close']} onClick={this.changeValue}> <img src={close} alt="" /> </div> </div> ); } } export default Child;
总结:
react组件的生命周期
- componentWillMount
- componentDidMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- componentDidUpdate
- componentWillUnmount
1. componentWillMount 组件将要挂载
组件刚经历constructor,初始完数据
在初始化渲染执行之前立刻调用,服务器端和客户端都只调用一次
组件还未进入render,组件还未渲染完成,dom还未渲染
如果在这个方法内调用setState,render() 将会感知到更新后的state,将会执行仅一次,尽管 state 改变了。
2. componentDidMount 组件渲染完成
在初始化渲染执行之后立刻调用一次,仅客户端有效(服务器端不会调用)
此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染
在生命周期中的这个时间点,组件拥有一个DOM 展现,你可以通过 this.getDOMNode() 来获取相应 DOM 节点
可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)
3.componentWillReceiveProps (nextProps)
在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
在接受父组件改变后的props需要重新渲染组件时用到的比较多
用此函数可以作为react 在 prop 传入之后, render() 渲染之前更新 state 的机会。
在该函数中调用 this.setState() 将不会引起第二次渲染。
通过对比nextProps和this.props,将nextProps setState为当前组件的state,从而重新渲染组件
componentWillReceiveProps (nextProps) { nextProps.openNotice !== this.props.openNotice && this.setState({ openNotice:nextProps.openNotice },() => { console.log(this.state.openNotice:nextProps) //将state更新为nextProps,在setState的第二个参数(回调)可以打印出新的state }) }
4. shouldComponentUpdate(nextProps,nextState)
唯一用于控制组件重新渲染的生命周期
由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程(暂时这么理解,其实setState以后有些情况并不会重新渲染,比如数组引用不变)。react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断。在这里return false可以阻止组件的更新
如果 shouldComponentUpdate 返回false,则 render() 将不会执行,直到下一次 state 改变。(另外,componentWillUpdate 和 componentDidUpdate 也不会被调用。)
在接收到新的props 或者 state,将要渲染之前调用。该方法在初始化渲染的时候不会调用,在使用 forceUpdate 方法的时候也不会。
默认情况下,shouldComponentUpdate 总会返回true,在 state 改变的时候避免细微的bug,但是如果总是小心地把 state 当做不可变的,在 render() 中只从 props 和state 读取值,此时你可以覆盖 shouldComponentUpdate 方法,实现新老 props 和state 的比对逻辑。
如果性能是个瓶颈,尤其是有几十个甚至上百个组件的时候,使用 shouldComponentUpdate可以提升应用的性能。
5. componentWillUpdate (nextProps,nextState)
在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
shouldComponentUpdate返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState
你不能在该方法中使用this.setState()。如果需要更新state来响应某个prop的改变,可使用componentWillReceiveProps。
6. componentDidUpdate(prevProps,prevState)
在组件完成更新后立即调用。在初始化时不会被调用。
组件更新完毕后,react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期
在组件的更新已经同步到DOM 中之后立刻被调用。
这里可以拿到prevProps和prevState,即更新前的props和state。
使用该方法可以在组件更新之后操作DOM 元素。
为了兼容 v0.9,DOM节点会作为最后一个参数传入。如果使用这个方法,你仍然可以使用 this.getDOMNode() 来访问 DOM 节点。
7.componentWillUnmount()
在组件从DOM 中移除的时候立刻被调用。
在该方法中执行任何必要的清理,比如无效的定时器,或者清除在 componentDidMount 中创建的 DOM 元素,移除所有组建中的监听 removeEventListener
除此之外,
8.constructor
constructor参数接受两个参数props,context,可以获取到父组件传下来的的props,context,
如果你想在constructor构造函数内部使用props或context,则需要传入,并传入super对象。
9. render函数
render函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,
在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染
react16中 render函数允许返回一个数组,单个字符串等,不在只限制为一个顶级DOM节点,可以减少很多不必要的div
组件生命周期的执行顺序:
在react的组件挂载及render过程中,最底层的子组件是最先完成挂载及更新的。
constructor()构造函数、componentWillMount执行顺序:顶层父组件 ->子组件 ->子组件 ->底层子组件
render、componentDidMount顺序:底层子组件 ->子组件 ->子组件 ->顶层父组件
以下是http://www.runoob.com菜鸟教程上面的一个实例:
class Button extends React.Component { constructor(props) { super(props); this.state = {data: 0}; this.setNewNumber = this.setNewNumber.bind(this); } setNewNumber() { this.setState({data: this.state.data + 1}) } render() { return ( <div> <button onClick = {this.setNewNumber}>INCREMENT</button> <Content myNumber = {this.state.data}></Content> </div> ); } } class Content extends React.Component { componentWillMount() { console.log('Component WILL MOUNT!') } componentDidMount() { console.log('Component DID MOUNT!') } componentWillReceiveProps(newProps) { console.log('Component WILL RECEIVE PROPS!') } shouldComponentUpdate(newProps, newState) { return true; } componentWillUpdate(nextProps, nextState) { console.log('Component WILL UPDATE!'); } componentDidUpdate(prevProps, prevState) { console.log('Component DID UPDATE!') } componentWillUnmount() { console.log('Component WILL UNMOUNT!') } render() { return ( <div> <h3>{this.props.myNumber}</h3> </div> ); } } ReactDOM.render( <div> <Button /> </div>, document.getElementById('example') );
运行结果:
(控制台info)
点击按钮:
参考:https://www.jianshu.com/p/c9bc994933d5
https://www.jianshu.com/p/ee122bb5b14b
http://www.runoob.com/react/react-component-life-cycle.html