You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
classScrollingListextendsReact.Component{constructor(props){super(props);this.listRef=React.createRef();}getSnapshotBeforeUpdate(prevProps,prevState){// Are we adding new items to the list?// Capture the scroll position so we can adjust scroll later.if(prevProps.list.length<this.props.list.length){constlist=this.listRef.current;returnlist.scrollHeight-list.scrollTop;}returnnull;}componentDidUpdate(prevProps,prevState,snapshot){// If we have a snapshot value, we've just added new items.// Adjust scroll so these new items don't push the old ones out of view.// (snapshot here is the value returned from getSnapshotBeforeUpdate)if(snapshot!==null){constlist=this.listRef.current;list.scrollTop=list.scrollHeight-snapshot;}}render(){return(<divref={this.listRef}>{/* ...contents... */}</div>);}}
componentDidUpdate(prevProps){// 来自网络if(prevProps.myProps!==this.props.myProp){// this.props.myProp has a different value// we can perform any operations that would // need the new value and/or cause side-effects // like AJAX calls with the new value - this.props.myProp}}
componentDidCatch(error,info){// from react.org// Display fallback UIthis.setState({hasError: true});// You can also log the error to an error reporting servicelogErrorToMyService(error,info);}
你有没有遇到过这样的问题:
读完本文希望你能对React的组件生命周期有一定的了解,编写React代码的时候能够更加得心应手,注意本文的生命周期讲的主要是浏览器端渲染,这是后端和全栈的主要使用方式,服务端渲染有些不一样,请注意区分,我们会在文中进行简单说明。
生命周期
如果你做过安卓开发方面的编程,那么你应该了解onCreate,onResume,onDestrory等常见生命周期方法,生命周期函数说白了就是让我们在一个组件的各个阶段都提供一些钩子函数来让开发者在合适的时间点可以介入并进行一些操作,比如初始化(onCreate)的时候我们应该初始化组件相关的状态和变量,组件要销毁(onDestrory)时,我们应该把一些数据结构销毁掉以节约内存,防止后台任务一直运行。在java类中也存在一个最常见的钩子函数contructor,你可以在这里调用super方法初始化父类,也可以在这里初始化各种变量。
我们先看下下面的图建立一个React组件生命周期的直观认识,图为React 16的生命周期,总的来说React组件的生命周期分为三个部分:
装载期间(Mounting)
,更新期间(Updating)
和卸载期间(Unmounting)
,React16多出来一个componentDidCatch() 函数用于捕捉错误。知道什么时候去使用哪些生命周期函数对于掌握和理解React是非常重要的,你可以看到这些生命周期函数有一定的规律,比如在某件事情发生之前调用的会用xxxWillxxx,而在这之后发生的会用xxxDidxxx。// 图来源于网络(侵删)
接下来我们就这三个阶段分别介绍一下各个生命周期函数,详细的生命周期函数解释可以看官方文档 React.Component。
装载期间
组件被实例化并挂载在到DOM树这一过程称为装载,在装载期调用的生命周期函数依次为
上图中还有一些函数比如
getDefaultProps
,getInitialState
等是在你不是用ES6的class创建组件而是用createReactClass
函数创建函数时暴露的方法,分别用于定义属性和设置初始状态,详见React-without-es6,这里我们不再赘述。通常推荐使用继承组件类的方式进行组件创建,即
class Analysis extends Component{}
constructor(props)
构造函数,和java class的构造函数一样,用于初始化这个组件的一些状态和操作,如果你是通过继承
React.Component
子类来创建React的组件的,那么你应当首先调用super(props)
初始化父类。在contructor函数中,你可以__初始化state__,比如
this.state = {xxx};
,不要在构造函数中使用setState()函数,强行使用的话React会报错。其次你可以在构造函数中__进行函数bind__,如:一个示例contructor实现如下:
如果你不需要初始化状态也不需要绑定handle函数的this,那么你可以不实现constructor函数,由默认实现代替。
关于bind函数的解释说明
注意js的this指向比较特殊,比如以下的例子作为onClick回调函数由button组件去调用的时候不会把组件类的上下文带过去。
这种问题推荐三种可能的解决方式,其核心均为将函数的this强制绑定到组件类上:
getDerivedStateFromProps()
这个函数会在render函数被调用之前调用,包括第一次的初始化组件以及后续的更新过程中,每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state。
该方法主要用来替代componentWillReceiveProps方法,willReceiveProps经常被误用,导致了一些问题,因此在新版本中被标记为unsafe。以掘金上的🌰为例,componentWillReceiveProps的常见用法如下,根据传进来的属性值判断是否要load新的数据
但这个方法的一个问题是外部组件多次频繁更新传入多次不同的 props,而该组件将这些更新 batch 后仅仅触发单次自己的更新,这种写法会导致不必要的异步请求,相比下来getDerivedStateFromProps配合componentDidUpdate的写法如下:
这种方式只在更新触发后请求数据,相比下来更节省资源。
render()
这是React组件的核心方法,用于根据状态state和属性props渲染一个React组件。我们应该保持该方法的纯洁性,这会让我们的组件更易于理解,只要state和props不变,每次调用render返回的结果应当相同,所以请__不要在render方法中改变组件状态,也不要在在这个方法中和浏览器直接交互__。
componentDidMount()
componentDidMount方法会在render方法之后立即被调用,该方法在整个React生命周期中只会被调用一次。React的组件树是一个树形结构,此时你可以认为这个组件以及他下面的所有子组件都已经渲染完了,所以在这个方法中你可以调用和真实DOM相关的操作了。
有些组件的启动工作是依赖 DOM 的,例如动画的启动,而
componentWillMount
的时候组件还没挂载完成,所以没法进行这些启动工作,这时候就可以把这些操作放在componentDidMount
当中。我们推荐可以在这个函数中__发送异步请求__,在回调函数中调用setState()设置state,等数据到达后触发重新渲染。但注意尽量__不要__在这个函数中__直接调用__setState()设置状态,这会触发一次额外的重新渲染,可能造成性能问题。
下面的代码演示了如何在componentDidMount加载数据并设置状态:
更新期间
当组件的状态或属性变化时会触发更新,更新过程中会依次调用以下方法:
shouldComponentUpdate(nextProps, nextState)
你可以用这个方法来告诉React是否要进行下一次render(),默认这个函数放回true,即每次更新状态和属性的时候都进行组件更新。注意这个函数如果返回false并不会导致子组件也不更新。
这个钩子函数__一般不需要实现, __如果你的组件性能比较差或者渲染比较耗时,你可以考虑使React.PureComponent 重新实现该组件,PureComponent默认实现了一个版本的shouldComponentUpdate会进行state和props的比较。当然如果你有自信,可以自己实现比较nextProps和nextState是否发生了改变。
该函数通常是优化性能的紧急出口,是个大招,不要轻易用,如果要用可以参考Immutable 详解及 React 中实践 .
getSnapshotBeforeUpdate()
该方法的触发时间为update发生的时候,在render之后dom渲染之前返回一个值,作为componentDidUpdate的第三个参数。该函数与 componentDidUpdate 一起使用可以取代 componentWillUpdate 的所有功能,比如以下是官方的例子:
componentDidUpdate(prevProps, prevState, snapshot)
该方法会在更新完成后被立即调用,你可以在这个方法中进行__DOM操作__,或者__做一些异步调用。__这个和首次装载过程后调用componentDidMount是类似的,不一样的是你可能需要判断下属性是否变化了再发起网络请求,如:
卸载期间
卸载期间是指组件被从DOM树中移除时,调用的相关方法为:
componentWillUnmount()
该方法会在组件被卸载之前被调用,如果你学过C++,那么这玩意和析构函数差不多,在方法里清理内存之类的,当然如果你用java请不用在意。如上所述,你可以在这个函数中进行相关清理工作,比如删除定时器之类的。
下面给个示例代码:
错误捕获
React16中新增了一个生命周期函数:
componentDidCatch(error, info)
在react组件中如果产生的错误没有被被捕获会被抛给上层组件,如果上层也不处理的话就会抛到顶层导致浏览器白屏错误,在React16中我们可以实现这个方法来捕获__子组件__产生的错误,然后在父组件中妥善处理,比如搞个弹层通知用户网页崩溃等。
在这个函数中请只进行错误恢复相关的处理,不要做其他流程控制方面的操作。比如:
React16中的生命周期函数变化
componentWillMount,componentWillUpdate, componentWillReceiveProps等生命周期方法在下个主版本中会被废弃?
根据这份RFC,是的,这些生命周期方法被认为是不安全的,在React16中被重命名为UNSAFE_componentWillMount,UNSAFE_componentWillUpdate,UNSAFE_componentWillReceiveProps,而在更下个大版本中他们会被废弃。详见 React 16.3版本发布公告。
总结
总结一下,以上讲的这些生命周期都有自己存在的意义,但在React使用过程中我们最常用到的生命周期函数是如下几个:
其他的逻辑一般和用户的操作有关(各种handleClickXXXX),当然需要用到其他生命周期函数可以按需正确使用。如果阅读文章过程中遇到问题欢迎评论进行修正。
参考文章
The text was updated successfully, but these errors were encountered: