手写react4-合成事件

如果希望setState之后可以用上上一次的值,可以给setState传函数

  handleClick = (syntheticEvent) => {
    this.setState(state => ({ number: this.state.number + 1 }));
    this.setState(state => ({ number: this.state.number + 1 }));
复制代码

setState什么是同步的 什么时候是异步的

  • 在React能管辖的地方就是批量异步的,比如事件处理函数,比如生命周期函数异步

  • 在React管不到的地方,就是同步的,例如setTimeout setInterval 原生事件处理函数

handleClick = (syntheticEvent) => {
  setTimeout(() => {//在setTimeout是同步更新的
    this.setState({ number: this.state.number + 1 });
    console.log(this.state.number);
    this.setState({ number: this.state.number + 1 });
    console.log(this.state.number);
  });
复制代码

批量更新的实现依赖于一个全局变量:

export let updateQueue = {
    isBatchingUpdate =false,//默认值是非批量的,同步的
    updaters: [],//更新器的数组
    batchUpdate() {
        for (let updater of updateQueue.updaters) {
            updater.updateComponent();
        }
        updateQueue.updaters.length = 0;
        updateQueue.isBatchingUpdate = false;
    }
}
复制代码

在emitUpdate中需要增加判断,如果是批量更新,则将Updater对象都暂存在updateQueue.updaters中

class Updater {
    constructor(classInstance) {
        this.classInstance = classInstance;
        this.pendingStates = [];//等待生效的数组
        //this.callbacks = [];
    }
    addState(partialState) {
        this.pendingStates.push(partialState);
        this.emitUpdate();// 触发更新
    }
    emitUpdate() {
        //有可能是批量异步更新,也有可能是同步更新
        if (updateQueue.isBatchingUpdate) {//批量异步更新
            updateQueue.updaters.push(this);//不刷 新组件视图了,只是把自己这个updater实例添加到updateQueue等待生效
        } else {//同步直接更新
            this.updateComponent();
        }
    }
复制代码

思路:在合成事件触发以前,将updateQueue.isBatchingUpdate改为true,批量更新完后再改回去

之前的版本中,要向button上绑定click事件

现在不向button上绑定事件了

而是把所有的事件都绑到document上

React17以前事件都委托到document上

React17之后事件都委托到容器上 <divid=”container”>

为什么 是因为在一个页面中可以存在多个不同的React应用

<divid=”root1″> ReactDOM.render(

,root1);

<divid=”root2″>ReactDOM.render(

,root2);

相当于一个事件委托

1.可以实现刚才说的,事件开始

//updateQueue.isBatchingUpdate = true;

结束 的时候

//updateQueue.batchUpdate();

2.可以做一些浏览器兼容性处理

不同浏览器API不一样的

把不同的事件对象做成一个标准化的事件对象,提供标准的API访问供用户使用。

需要更新updateProps

function updateProps(dom, oldProps, newProps) {
    for (let key in newProps) {
        if (key === 'children') {//children
            continue;//此处忽略子节点的处理
        } else if (key === 'style') {//style
            let styleObj = newProps[key];
            for (let attr in styleObj) {
                dom.style[attr] = styleObj[attr];
            }
        } else if (key.startsWith('on')) {
            //dom[key.toLocaleLowerCase()] = newProps[key];
            addEvent(dom, key.toLocaleLowerCase(), newProps[key]);
复制代码

新建一个event.js文件,专门用来存储事件相关的方法:

import { updateQueue } from './Component';
function addEvent(dom, eventType, eventHandler) {
    let store;
    if (dom._store) {
        store = dom._store;
    } else {
        dom._store = {};
        store = dom._store;
    }
    //store.onclick=handleClick
    store[eventType] = eventHandler;
    //document.onclick=dispatchEvent
    if (!document[eventType]) {
        document[eventType] = dispatchEvent;
    }
}
/**
 * 不管点什么按钮,触发什么事件,最终执行的都是dispatchEvent
 * 在合成事件的处理函数里,状态的更新是批量的
 * @param {*} event 原生的事件对象 不同的浏览可能是不一样
 */
function dispatchEvent(event) {
    //target =button type ==click
    let { target, type } = event;
    let eventType = 'on' + type;
    //先把批量更新 全局变量设置为true
    updateQueue.isBatchingUpdate = true;

    let syntheticEvent = createSyntheticEvent(event);
    //获取事件源DOM对象上的store属性
    let { store } = target;
    let eventHandler = store && store[eventType];//handleClick
    eventHandler && eventHandler.call(target, syntheticEvent);//handleClick(syntheticEvent);

    updateQueue.isBatchingUpdate = false;
    updateQueue.batchUpdate();//进行真正的更新
}

function createSyntheticEvent(nativeEvent) {
    let syntheticEvent = { nativeEvent };
    for (let key in nativeEvent) {
        syntheticEvent[key] = nativeEvent[key];
    }
    //此处会有一些兼容性处理
    return syntheticEvent;
}
复制代码

流程:

用户点击onClick handleClick -> dispatchEvent

-> handleClick(syntheticEvent) -> setState -> updater.addState -> Updater.emitUpdate -> updateQueue.updaters.push(Updater)

-> updateQueue.batchUpdate

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享