美文网首页
【React】每个人都可以理解的合成事件与setState机制

【React】每个人都可以理解的合成事件与setState机制

作者: 南慕瑶 | 来源:发表于2020-05-06 18:09 被阅读0次

    这是一篇给小白的,极为通俗易懂的个人理解总结。

    当年初学 react,学习 setState 机制,总是被一堆源码中的函数名搞的一头雾水。

    这里没有任何源码,只有经过个人理解,总结出来的简易流程。

    希望能够帮助到,和曾经的自己(现在也差不多),一样想要精进、却举步维艰的你们~

    我们从一道常见的面试题入手:

    react 的合成事件,是如何映射到真实 dom 上的?

    我们以一个简版的例子,看一下从用户点击动作开始,都发生了什么~

    定义一个container:App

    import React, { Component } from 'react'

    class App extends Component {

      constructor(props) {

        super(props)

        this.state = {

          count: 0,

        }

        this.clickHandler = this.clickHandler.bind(this)

      }

      clickHandler() {

        console.log('count1:', this.state.count)

        this.setState({

          count: 1,

        })

        console.log('count2:', this.state.count)

        this.setState({

          count: 2,

        })

        console.log('count3:', this.state.count)

      }

      render() {

        console.log('count render', this.state.count)

        return <button onClick={this.clickHandler}>点我更新count</button>

      }

    }

    export default App

    旅程开始~

    1.前言:元素挂载

    点击元素前,得先有元素。so,先简单看一下元素挂载过程~

    ReactDOM.render() 方法,解析 jsx 生成 virtual dom tree(React16中,又由 virtual dom tree 生成 fiber tree),最终将真实的节点渲染到页面上。

    在这个过程中,react 内部,会在每个真实的 dom 元素上,偷偷地添加一些属性,用来配合它搞一些事情。

    其中,除了root根节点,其他所有元素,都被添加了这么个属性:__reactEventHandlers$*******

    这里,我们直接读取了 button 元素的这个属性。

    似乎有点剧透了,咳咳~  ┓( ´∀` )┏

    下面,进入正题👇

    2.事件触发

    react 基于事件冒泡,统一在 document 上插入了原生的事件监听方法,用于捕获页面任意位置的用户操作。

    【注】

    经实测,这里给 document 插入的原生事件监听,取决于子元素设置了什么合成事件监听。

    每个合成事件都有对应的原生事件,以此给 document 添加上需要的原生事件监听函数。

    当用户点击 button,click 事件顺着 dom 树结构冒泡到 document 上,document 上的原生 onclick 事件响应函数被触发。

    这个响应函数做了什么事呢?

    大概是这样:

    function documentClickHandler(e) {

        // 获取真正触发点击事件的元素节点

        const target = e.target

        // 执行元素节点上注册的合成事件响应函数

        target.__reactEventHandlers$*******.onClick()

    }

    ok,事情的本质,其实就是这么简单。

    到这里,那个面试题的答案,已经很清晰了。

    但是整个流程并没有结束,我们继续,看看 setState 这个小家伙,一会儿同步、一会儿异步,到底是在干啥。

    3.执行合成事件响应函数:clickHandler

     clickHandler() {

        console.log('count1:', this.state.count)

        this.setState({

          count: 1,

        })

        console.log('count2:', this.state.count)

        this.setState({

          count: 2,

        })

        console.log('count3:', this.state.count)

      }

    这里,连续调用了两次 setState。但是,打印出的 count1、count2、count3,全部为 0。

    这就是典型的,所谓 setState 的异步现象:调用 setState 后,state 的值并没有立即更新。

    4.合成事件中的 setState

    以下代码,由个人对这个流程的简化理解而来,全部是伪代码。并非由源码精简而来。

    这里的简单例子,只为简单说明整体逻辑流程。

    如有理解偏差,烦请指正。

    let flag = false // 相当于源码的 isBatchingUpdates

    function documentClickHandler(e) {

        // 获取真正触发点击事件的元素节点

        const target = e.target

        // 设置标记

        flag = true

        // 执行元素节点上注册的合成事件响应函数

        target.__reactEventHandlers$*******.onClick()

        // 置回标记

        flag = false

        // 触发 setState,批量更新在 onClick 中缓存的那些 state

        setState()

    }

    const arr = [ ]   // 相当于源码的 dirtyComponents

    // 开发者调用的setState,也是它👇

    function setState(state) {

        // 如果标记是 true,就先不做更新。缓存当前 state,等到 flag 为 false 的时候,做批量更新

        if (flag) {

            arr.push(state)

        } else {

            // 这里,没有传 state,就说明是合成事件执行完,调用进来的。批量更新缓存的那些 state

            if (!state) {

                 let stateObj = {}

                  arr.forEach(state => {

                         stateObj = Object.assign(stateObj, state)

                  })

                // 真正执行 setState(我臆想的方法。。总之应该会有个类似的东西。。)

                doSetState(stateObj)

            } else {          

                doSetState(state)

            }

        }

    }

    【注】

    react 生命周期中的 setState,同理。

    比如 componentDidMount 中连续调用了两次 setState,在组件挂载过程中,需要执行 componentDidMount 这个生命周期时:

    doRender() {

        // 做各种挂载需要的逻辑

        // ......    

        // 挂载完成,执行生命周期函数。和合成事件的执行同理,用 flag 标记,告诉 setState 走批量更新

        flag = true

        componentDidMount()    

        flag = false

    }

    ok,旅程结束~

    如有理解偏差,请大佬们指教。感谢。

    相关文章

      网友评论

          本文标题:【React】每个人都可以理解的合成事件与setState机制

          本文链接:https://www.haomeiwen.com/subject/wtlwghtx.html