拥抱react新生命周期--getDerivedStateFromProps

栏目: 服务器 · 发布时间: 5年前

内容简介:作为一个前端开发,从来不能够说自己已经学不动了,新的事物每天都在出现,原本已经熟稔的框架也会时不时出来骚扰你一下。React 16是最近一年多React更新最大的版本。除了让大家喜闻乐见的向下兼容的Fiber,防止了客户端react在进行渲染的时候阻塞页面的其他交互行为。Fiber源码速览不过Fiber的更新是不可见的,我们只需要了解他实现Fiber的大概思路,并不会影响到我们日常的业务开发工作。

作为一个前端开发,从来不能够说自己已经学不动了,新的事物每天都在出现,原本已经熟稔的框架也会时不时出来骚扰你一下。

拥抱react新生命周期--getDerivedStateFromProps

React 16是最近一年多React更新最大的版本。除了让大家喜闻乐见的向下兼容的Fiber,防止了客户端react在进行渲染的时候阻塞页面的其他交互行为。Fiber源码速览

不过Fiber的更新是不可见的,我们只需要了解他实现Fiber的大概思路,并不会影响到我们日常的业务开发工作。

但是除了Fiber,整个React生命周期的变化对于所有开发者来说才是最可见的。目前的业务环境已经更新到了React 16.4,

拥抱react新生命周期--getDerivedStateFromProps

从change log中我们可以看到React DOM的更新, getDerivedStateFromProps 的修改,无论如何,这个函数都会在re-rendering之进行调用。

componentWillReceiveProps 这个历史遗留即将在未来被标记为 deprecated ,那么为了新的工程能够保证在未来不需要进行代码的重构,所以现在就需要开始拥抱新的生命周期函数了。

新的生命周期过程

先来看看最新版本react的生命周期图:

拥抱react新生命周期--getDerivedStateFromProps

对于已经比较了解react的童鞋,这个图应该看起来非常熟悉了(虽然我画的很丑。。)

变化在于+2 生命周期,-3 UNSAFE。

新增: getDerivedStateFromPropsgetSnapshotBeforeUpdate UNSAFE: UNSAFE_componentWillMountUNSAFE_componentWillUpdateUNSAFE_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 会在两种驱动状态下被重新渲染。

当这个函数被多次调用的时候,就需要注意到, stateprops 的变化将会怎样影响到你的组件变化。

// 组件接收一个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;
}
复制代码

如何解决这个棘手的问题呢?

  1. 好好组织你的组件,在非必须的时候,摒弃这种写法。 type 要么由 props 驱动,要么完全由 state 驱动。
  2. 如果实在没有办法解耦,那么就需要一个hack来辅助:绑定 propsstate 上。
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更希望将受控的 propsstate 进行分离,就如同 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 中根据新的 propsstate 来进行异步操作,比如从服务端拉取数据。

// 在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不断更新并且往好的方向发展才是对前端社区最有利的事情,我们这些开发者们能够稳定的使用一个前端框架在几年前都是奢望,从 backbonejQuery 再到 Angular ,鬼知道我们这几年都经历了什么, Angular 2 的更新让我放弃了这个使用了很久的框架。希望react和vue能够持续更新下去吧~

拥抱react新生命周期--getDerivedStateFromProps

以上所述就是小编给大家介绍的《拥抱react新生命周期--getDerivedStateFromProps》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

C专家编程

C专家编程

Peter Van Der Linden / 徐波 / 人民邮电出版社 / 2008-2 / 45.00元

《C专家编程》展示了最优秀的C程序员所使用的编码技巧,并专门开辟了一章对C++的基础知识进行了介绍。 书中C的历史、语言特性、声明、数组、指针、链接、运行时、内存以及如何进一步学习C++等问题进行了细致的讲解和深入的分析。全书撷取几十个实例进行讲解,对C程序员具有非常高的实用价值。 本书可以帮助有一定经验的C程序员成为C编程方面的专家,对于具备相当的C语言基础的程序员,本书可以帮助他们......一起来看看 《C专家编程》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具