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'))