深入Preact源码分析(四)setState发生了什么

标签: Preact 源码
4人阅读 评论(0) 收藏 举报
分类:

setState发生了什么

setState(state, callback) {
    let s = this.state;
    if (!this.prevState) this.prevState = extend({}, s);
    extend(s, typeof state==='function' ? state(s, this.props) : state);// 语句3
    if (callback) (this._renderCallbacks = (this._renderCallbacks || [])).push(callback);
    enqueueRender(this);
},

setState的定义如上,代码逻辑很容易看出

1、prevState若不存在,将要更新的state合并到prevState上

2、可以看出Preact中setState参数也是可以接收函数作为参数的。将要更新的state合并到当前的state

3、如果提供了回调函数,则将回调函数放进_renderCallbacks队列

4、调用enqueueRender进行组件更新

why?我刚看到setState的第2、3行代码的时候也是一脸蒙蔽。为什么它要这样又搞一个this.prevState又搞一个this.state,又有个state呢?WTF。
通过理清Preact的setState的执行原理。

应该是用于处理一个组件在一次流程中调用了两次setState的情况。

// 例如这里的handleClick是绑定click事件

handleClick = () =>{
    // 注意,preact中setState后state的值是会马上更新的
    this.setState({a:this.state.a+1});
    console.log(this.state.a);
    this.setState({a:this.state.a+1});
    console.log(this.state.a);
} 

基本上每一个学react的人,都知道上述代码函数在react中执行之后a的值只会加一,but!!!!在Preact中是加2的!!!!通过分析Preact的setState可以解释这个原因。
在上面的语句3,extend函数调用后,当前的state值已经改变了。但是即使state的值改变了,但是多次setState仍然是会只进行一次组件的更新(通过setTimeout把更新操作放在当前事件循环的最后),以最新的state为准。所以,这里的prevState应该是用于记录当前setState之前的上一次state的值,用于后面的diff计算。在enqueueRender执行diff时比较prevState和当前state的值

关于enqueueRender的相关定义

let items = [];

export function enqueueRender(component) {
    // dirty 为true表明这个组件重新渲染
    if (!component._dirty && (component._dirty = true) && items.push(component) == 1) {//语句1
        // 只会执行一遍
        (options.debounceRendering || defer)(rerender); // 相当于setTimeout render 语句2
    }
}

export function rerender() {
    let p, list = items;
    items = [];
    while ((p = list.pop())) {
        if (p._dirty) renderComponent(p);
    }
}

enqueueRender的逻辑主要是

1、语句1: 将调用了setState的组件的_dirty属性设置为false。通过这段代码我们还可以发现,
如果在一次流程中,调用了多次setState,rerender函数实际上还是只执行了一遍(通过判断component._dirty的值来保证一个组件内的多次setState只执行一遍rerender和判断items.push(component) == 1确保如果存在父组件调用setState,然后它的子组件也调用了setState,还是只会执行一次rerender)。items队列是用来存放当前所有dirty组件。

2、语句2。可以看作是setTimeout,将rerender函数放在本次事件循环结束后执行。rerender函数对所有的dirty组件执
renderComponent进行组件更新。

在renderComponent中将会执行的代码。只列出和初次渲染时有区别的主要部分

export function renderComponent(component, opts=undefined, mountAll=undefined, isChild=undefined) {
    ....
    if (isUpdate) {
        component.props = previousProps;
        component.state = previousState;
        component.context = previousContext;
        if (opts !== FORCE_RENDER && // FORCE_RENDER是在调用组件的forceUpdate时设置的状态位
            component.shouldComponentUpdate &&
            component.shouldComponentUpdate(props, state, context) === false) {
            skip = true;// 如果shouldComponentUpdate返回了false,设置skip标志为为true,后面的渲染部分将会被跳过
        } else if (component.componentWillUpdate) {
            component.componentWillUpdate(props, state, context);//执行componentWillUpdate生命周期函数
        }

        // 更新组件的props state context。因为componentWillUpdate里面有可能再次去修改它们的值
        component.props = props;
        component.state = state;
        component.context = context;
    }
    ....
    component._dirty = false;
    ....
    // 省略了diff渲染和dom更新部分代码
    ...
    if (!skip) {
        if (component.componentDidUpdate) {
            //componentDidUpdate生命周期函数
            component.componentDidUpdate(previousProps, previousState, previousContext);
        }
    }

    if (component._renderCallbacks != null) {
        // 执行setState的回调
        while (component._renderCallbacks.length) component._renderCallbacks.pop().call(component);
    }
}

逻辑看代码注释就很清晰了。先shouldComponentUpdate生命周期,根据返回值决定是都否更新(通过skip标志位)。然后将组件的_dirty设置为true表明已经更新了该组件。然后diff组件更新,执行componentDidUpdate生命周期,最后执行setState传进的callback。

流程图如下:

这里写图片描述

下一步,就是研究setState组件进行更新时的diff算法干了啥

查看评论

React与Preact差异之 -- setState

Preact是React的轻量级实现,是React比较好的替代者之一,有着体积小的优点,当然与React之间一定会存在实现上的差异,本文介绍了在 setState 方面的差异之处。 源码分析 ...
  • qq3401247010
  • qq3401247010
  • 2017-10-23 12:29:55
  • 174

setState详解

我们都知道,React通过this.state来访问state,通过this.setState()方法来更新state。当this.setState()方法被调用的时候,React会重新调用rende...
  • sysuzhyupeng
  • sysuzhyupeng
  • 2017-03-18 09:43:47
  • 11939

「React」与(Preact)差异之setState

react是React的轻量级实现,是React比较好的替代者之一,有着体积小的优点,当然与React之间一定会存在实现上的差异,本文介绍了在 setState 方面的差异之处。React关键代码:s...
  • Bg70PVnyBv1
  • Bg70PVnyBv1
  • 2018-03-23 00:00:00
  • 45

多个setState方法的调用原理

React中通过使用setState来更新组件的状态,触发render setState被调用时发生了什么? React首先将你传递给setState函数的对象合并到当前状态中,然后创建一个Rea...
  • kongjunchao159
  • kongjunchao159
  • 2017-05-22 15:44:08
  • 1135

《深入理解计算机系统》——Hello world到底经历了什么?

最近开始在看《深入理解计算机系统》,因为虽然每天在写代码,但实在有太多不明白的东西,很想从底层了解,写完的代码到底经历了怎样一段旅程,最终达到了页面上的效果,否则总觉得自己在闭着眼睛写代码,眼不盲心却...
  • orange_linmama
  • orange_linmama
  • 2016-05-29 01:30:07
  • 852

React源码分析5 — setState机制

1 概述React作为一门前端框架,虽然只是focus在MVVM中的View部分,但还是实现了View和model的绑定。修改数据的同时,可以实现View的刷新。这大大简化了我们的逻辑,只用关心数据流...
  • u013510838
  • u013510838
  • 2017-03-02 11:57:31
  • 2790

React学习-- React源码(4)setState机制

React通过this.state来访问state,通过this.setState来更新state。当setState被调用的时候,React会重新调用render方法来重新渲染组件。setState...
  • u014328357
  • u014328357
  • 2017-06-15 16:47:19
  • 633

React setState(多次调用setState真的会影响性能吗?)

看了网上很多帖子很多都说setState慎用,因为可能导致页面性能变差。 报着怀疑的态度发现并不如此。 代码和运行结果直接看效果(后面再从源码角度进行分析)涉及到React的事件系统。 现象分析 1....
  • qq_26878975
  • qq_26878975
  • 2017-07-16 17:33:27
  • 1684

聊聊计算机启动时都发生了什么

聊聊计算机启动时都发生了什么 (资料来源:leetcode , 度娘等,还有些自己的语言整合,纯属个人聊聊) 介绍下基本输入输出系统 * 1)BIOS(Basic I/O system) ...
  • weixin_38739799
  • weixin_38739799
  • 2017-10-08 22:08:03
  • 87

js中new一个函数的时候,到底发生了什么

function x(){             debugger             this.w=1             this.a()         }         ...
  • THEANARKH
  • THEANARKH
  • 2016-07-20 02:02:04
  • 1970
    个人资料
    持之以恒
    等级:
    访问量: 6847
    积分: 491
    排名: 10万+
    文章存档