内容简介:作为一个前端开发,从来不能够说自己已经学不动了,新的事物每天都在出现,原本已经熟稔的框架也会时不时出来骚扰你一下。React 16是最近一年多React更新最大的版本。除了让大家喜闻乐见的向下兼容的Fiber,防止了客户端react在进行渲染的时候阻塞页面的其他交互行为。Fiber源码速览不过Fiber的更新是不可见的,我们只需要了解他实现Fiber的大概思路,并不会影响到我们日常的业务开发工作。
作为一个前端开发,从来不能够说自己已经学不动了,新的事物每天都在出现,原本已经熟稔的框架也会时不时出来骚扰你一下。
React 16是最近一年多React更新最大的版本。除了让大家喜闻乐见的向下兼容的Fiber,防止了客户端react在进行渲染的时候阻塞页面的其他交互行为。Fiber源码速览
不过Fiber的更新是不可见的,我们只需要了解他实现Fiber的大概思路,并不会影响到我们日常的业务开发工作。
但是除了Fiber,整个React生命周期的变化对于所有开发者来说才是最可见的。目前的业务环境已经更新到了React 16.4,
从change log中我们可以看到React DOM的更新, getDerivedStateFromProps
的修改,无论如何,这个函数都会在re-rendering之进行调用。
componentWillReceiveProps
这个历史遗留即将在未来被标记为 deprecated ,那么为了新的工程能够保证在未来不需要进行代码的重构,所以现在就需要开始拥抱新的生命周期函数了。
新的生命周期过程
先来看看最新版本react的生命周期图:
对于已经比较了解react的童鞋,这个图应该看起来非常熟悉了(虽然我画的很丑。。)
变化在于+2 生命周期,-3 UNSAFE。
新增: getDerivedStateFromProps
, getSnapshotBeforeUpdate
UNSAFE: UNSAFE_componentWillMount
, UNSAFE_componentWillUpdate
, UNSAFE_componentWillReceiveProps
。
getDerivedStateFromProps
React生命周期的命名一直都是非常语义化的,这个生命周期的意思就是 从props中获取state ,可以说是太简单易懂了。
可以说,这个生命周期的功能实际上就是将传入的 props
映射到 state
上面。
由于16.4的修改,这个函数会在每次re-rendering之前被调用,这意味着什么呢?
是的,这意味着即使你的 props
没有任何变化,而是 state
发生了变化,导致组件发生了re-render,这个生命周期函数依然会被调用。看似一个非常小的修改,却可能会导致很多隐含的问题。
使用
这个生命周期函数是为了替代 componentWillReceiveProps
存在的,所以在你需要使用 componentWillReceiveProps
的时候,就可以考虑使用 getDerivedStateFromProps
来进行替代了。
两者的参数是不相同的,而 getDerivedStateFromProps
是一个 静态函数 ,也就是这个函数不能通过 this
访问到 class
的属性,也并不推荐直接访问属性。而是应该通过参数提供的 nextProps
以及 prevState
来进行判断,根据新传入的 props
来映射到 state
。
需要注意的是,如果 props
传入的内容不需要影响到你的 state
,那么就需要返回一个 null
,这个返回值是必须的,所以尽量将其写到函数的末尾。
static getDerivedStateFromProps(nextProps, prevState) { const {type} = nextProps; // 当传入的type发生变化的时候,更新state if (type !== prevState.type) { return { type, }; } // 否则,对于state不进行任何操作 return null; } 复制代码
问题1 -- 多来源的不同状态
假设我们有一个列表,这个列表受到页面主体,也就是根组件的驱动,也受到其本身数据加载的驱动。
因为这个页面在开始渲染的时候,所有的数据请求可能是通过 batch
进行的,所以要在根组件进行统一处理,而其列表的分页操作,则是由其本身控制。
这会出现什么问题呢?该列表的状态受到两方面的控制,也就是re-render可能由 props
驱动,也可能由 state
驱动。这就导致了 getDerivedStateFromProps
会在两种驱动状态下被重新渲染。
当这个函数被多次调用的时候,就需要注意到, state
和 props
的变化将会怎样影响到你的组件变化。
// 组件接收一个type参数 static propTypes = { type: PropTypes.number } // 组件还具有自己的状态来渲染列表 class List extends React.Component { constructor(props) { super(props); this.state = { list: [], type: 0, } } } 复制代码
如上面代码的例子所示,组件既受控,又控制自己。当 type
发生变化,会触发一次 getDerivedStateFromProps
,在这里更新组件的 type
状态,然而,在进行异步操作之后,组件又会更新 list
状态,这时你的 getDerivedStateFromProps
函数就需要注意,不能够仅仅判断 type
是否变化来更新状态,因为 list
的变化也会更新到组件的状态。这时就必须返回一个 null
,否则会导致组件无法更新并且报错。
问题2 -- 组织好你的组件
考虑一下,如果你的组件内部既需要修改自己的 type
,有需要接收从外部修改的 type
。
是不是非常混乱? getDerivedStateFromProps
中你根本不知道该做什么。
static getDerivedStateFromProps(nextProps, prevState) { const {type} = nextProps; // type可能由props驱动,也可能由state驱动,这样判断会导致state驱动的type被回滚 if (type !== prevState.type) { return { type, }; } // 否则,对于state不进行任何操作 return null; } 复制代码
如何解决这个棘手的问题呢?
- 好好组织你的组件,在非必须的时候,摒弃这种写法。
type
要么由props
驱动,要么完全由state
驱动。 - 如果实在没有办法解耦,那么就需要一个hack来辅助:绑定
props
到state
上。
constructor(props) { super(props); this.state = { type: 0, props, } } static getDerivedStateFromProps(nextProps, prevState) { const {type, props} = nextProps; // 这段代码可能看起来非常混乱,这个props可以被当做缓存,仅用作判断 if (type !== props.type) { return { type, props: { type, }, }; } // 否则,对于state不进行任何操作 return null; } 复制代码
上面的代码可以保证在进行多数据源驱动的时候,状态能够正确改变。当然,这样的代码很多情况下是会影响到别人阅读你的代码的,对于维护造成了非常大的困难。
从这个生命周期的更新来看,react更希望将受控的 props
和 state
进行分离,就如同 Redux
作者Dan Abramov在redux文档当中写的一样 Presentational and Container Components ,将所有的组件分离称为展示型组件和容器型组件,一个只负责接收 props
来改变自己的样式,一个负责保持其整个模块的 state
。这样可以让代码更加清晰。但是在实际的业务逻辑中,我们有时很难做到这一点,而且这样可能会导致容器型组件变得非常庞大以致难以管理,如何进行取舍还是需要根据实际场景决定的。
问题3 -- 异步
以前,我们可以在 props
发生改变的时候,在 componentWillReceiveProps
中进行异步操作,将 props
的改变驱动到 state
的改变。
componentWillReceiveProps(nextProps) { if (props.type !== nextProps.type) { // 在这里进行异步操作或者更新状态 this.setState({ type: props.type, }); this._doAsyncOperation(); } } 复制代码
这样的写法已经使用了很久,并且并不会存在什么功能上的问题,但是将 componentWillReceiveProps
标记为 deprecated 的原因也并不是因为功能问题,而是性能问题。
当外部多个属性在很短的时间间隔之内多次变化,就会导致 componentWillReceiveProps
被多次调用。这个调用并不会被合并,如果这次内容都会触发异步请求,那么可能会导致多个异步请求阻塞。
getDerivedStateFromProps
is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.
这个生命周期函数会在每次调用 render
之前被触发,而读过一点react源码的童鞋都会了解,react对于渲染是有合并过程的,组件的更新过程是 batch
的,这样 render
触发的频率并不会非常频繁。
在使用 getDerivedStateFromProps
的时候,遇到了上面说的 props
在很短的时间内多次变化,也只会触发一次 render
,也就是只触发一次 getDerivedStateFromProps
。这样的优点不言而喻。
那么如何使用 getDerivedStateFromProps
进行异步的处理呢?
If you need to perform a side effect (for example, data fetching or an animation) in response to a change in props, use componentDidUpdate
lifecycle instead.
官方教你怎么写代码系列 ,但是其实也没有其他可以进行异步操作的地方了。为了响应 props
的变化,就需要在 componentDidUpdate
中根据新的 props
和 state
来进行异步操作,比如从服务端拉取数据。
// 在getDerivedStateFromProps中进行state的改变 static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.type !== prevState.type) { return { type: nextProps.type, }; } return null; } // 在componentDidUpdate中进行异步操作,驱动数据的变化 componentDidUpdate() { this._loadAsyncData({...this.state}); } 复制代码
以上
以上是本期开发过程中使用新的生命周期函数的时候遇到的一点小问题和一些相关思考。react为了防止部分开发者滥用生命周期,可谓非常尽心尽力了。既然你用不好,我就干脆不让你用。一个静态的生命周期函数可以让状态的修改更加规范和合理。
至于为什么全文没有提到 getSnapshotBeforeUpdate
,因为自己并没有用到#诚实脸。简单看了一下,这个函数返回一个update之前的快照,并且传入到 componentDidUpdate
中,组件更新前后的状态都可以在 componentDidUpdate
中获取了。一些需要在组件更新完成之后进行的操作所需要的数据,就可以不需要挂载到 state
或者是cache下来了。比如官方例子中所举例的保留更新之前的页面滚动距离,以便在组件update完成之后恢复其滚动位置。也是一个非常方便的周期函数。
最后
新周期新气象~,react不断更新并且往好的方向发展才是对前端社区最有利的事情,我们这些开发者们能够稳定的使用一个前端框架在几年前都是奢望,从 backbone
到 jQuery
再到 Angular
,鬼知道我们这几年都经历了什么, Angular 2
的更新让我放弃了这个使用了很久的框架。希望react和vue能够持续更新下去吧~
以上所述就是小编给大家介绍的《拥抱react新生命周期--getDerivedStateFromProps》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JavaScript凌厉开发
张鑫 黄灯桥、杨彦强 / 清华大学出版社 / 2010 年4月 / 49.00元
本书详细介绍Ext JS框架体系结构,以及利用HTML/CSS/JavaScript进行前端设计的方法和技巧。作者为Ext中文站站长领衔的三个国内Ext JS先锋,在开发思维和开发经验上有着无可争议的功力。 本书包含的内容有Ext.Element.*、事件Observable、Ext组件+MVC原理、Grid/Form/Tree/ComboBox、Ajax缓存Store等,并照顾JavaSc......一起来看看 《JavaScript凌厉开发》 这本书的介绍吧!