Skip to content

06.类组件的setState方法实现

具体代码演化过程请观看视频,这里呈现关键代码:

js
// Component.js
import { updateDomTree, findDomByVNode } from './react-dom'
export let updaterQueue = {
    isBatch: false,
    updaters: new Set()
}
export function flushUpdaterQueue(){
    updaterQueue.isBatch = false;
    for (let updater of updaterQueue.updaters) {
        updater.launchUpdate();
    }
    updaterQueue.updaters.clear();
}
class Updater {
    constructor(ClassComponentInstance) {
        this.ClassComponentInstance = ClassComponentInstance;
        this.pendingStates = [];
    }
    addState(partialState) {
        this.pendingStates.push(partialState);
        this.preHandleForUpdate();
    }
    preHandleForUpdate() {
        if (updaterQueue.isBatch) {//如果是批量
            updaterQueue.updaters.add(this);//就把当前的updater添加到set里保存
        } else {
            this.launchUpdate();
        }
    }
    launchUpdate() {
        const { ClassComponentInstance, pendingStates } = this;
        if (pendingStates.length === 0) return
        ClassComponentInstance.state = this.pendingStates.reduce((preState, newState) => {
            return {
                ...preState, ...newState
            }
        }, this.ClassComponentInstance.state);
        this.pendingStates.length = 0
        ClassComponentInstance.update();
    }
}
export class Component {
    static IS_CLASS_COMPONENT = true
    constructor(props) {
        this.props = props;
        this.state = {};
        this.updater = new Updater(this);
    }
    setState(partialState) {
        /**
         * this.state = { ...this.state, ...partialState };
         * this.update()
         */
        this.updater.addState(partialState);
    }
    update() {
        let oldVNode = this.oldVNode;
        let oldDOM = findDomByVNode(oldVNode);
        let newVNode = this.render();
        updateDomTree(oldDOM, newVNode)
        this.oldVNode = newVNode;
    }
}
js
// react-dom.js
export function createDOM(VNode){
    if(!VNode) return
    const {type, props} = VNode
    let dom;
    if (typeof type === 'function' && type.IS_CLASS_COMPONENT && VNode.$$typeof === REACT_ELEMENT){
        return getDomByClassComponent(VNode)
    } else if (typeof type === 'function' && VNode.$$typeof === REACT_ELEMENT){
        return getDomByFunctionComponent(VNode)
    } else if (type && VNode.$$typeof === REACT_ELEMENT) {
        dom = document.createElement(type);
    } 
    if(props){
        if (typeof props.children === 'object' && props.children.type) {
            mount(props.children, dom)
        } else if (Array.isArray(props.children)) {
            mountArray(props.children, dom);
        } else if (typeof props.children === 'string'){
            dom.appendChild(document.createTextNode(props.children));
        }
    }
    setPropsForDOM(dom, props)
    VNode.dom = dom
    return dom
}
function getDomByClassComponent(vNode){
    let { type, props } = vNode;
    let instance = new type(props)
    let renderVNode = instance.render();
    instance.oldVNode = renderVNode
    // 纯粹是为了测试,后续删除
    setTimeout(() => {
        instance.setState({xxx: '99999999'})
    }, 3000)
    if (!renderVNode) return null;
    return createDOM(renderVNode);
}
export function updateDomTree(oldDOM, newVNode){
    if(!oldDOM) return
    let parentNode = oldDOM.parentNode
    parentNode.removeChild(oldDOM);
    parentNode.appendChild(createDOM(newVNode))
}
export function findDomByVNode(VNode){
    if(!VNode) return
    if(VNode.dom) return VNode.dom
}
js
// index.js
import React from './react';
import ReactDOM from './react-dom';
class MyClassComponent extends React.Component{
    constructor(props) {
        super(props);
        this.state = { xxx: '999' };
    }
    render(){
        return <div className='test-class' style={{color: 'red'}}>Simple React App {this.state.xxx}</div>
    }
}
ReactDOM.render(<MyClassComponent />, document.getElementById('root'))

基于 VitePress 构建