组件更新
- 组件更新判断逻辑如下图:

1. 合并state
1.1. src/index.js
import React from './react';
import ReactDOM from './react-dom';
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 0 };
setTimeout(() => {
this.setState({
number: this.state.number + 1 });
console.log(this.state);
}, 1000);
}
render() {
return <div id={
'counter' + this.state.number}>+</div>
}
}
ReactDOM.render(
<Counter />,
document.getElementById('root')
);
1.2. src/react/component.js
export const updateQueue = {
updaters: [],
isPending: false,
};
class Updater {
constructor(componentInstance) {
this.componentInstance = componentInstance;
this.pendingStates = [];
this.nextProps = null;
}
addState(partialState){
this.pendingStates.push(partialState);
this.emitUpdate();
}
emitUpdate(nextProps){
this.nextProps = nextProps;
if (nextProps || !updateQueue.isPending) {
this.updateComponent();
} else {
updateQueue.add(this);
}
}
updateComponent(){
let {
componentInstance, pendingStates, nextProps } = this;
if (nextProps || pendingStates.length > 0) {
shouldUpdate(componentInstance, nextProps, this.getState());
}
}
getState(){
let {
componentInstance, pendingStates } = this;
let {
state } = componentInstance;
if (pendingStates.length > 0) {
for (let i = 0; i < pendingStates.length; i++) {
let nextState = pendingStates[i];
if (typeof nextState === 'function') {
state = {
...state, ...nextState.call(componentInstance, state) };
} else {
state = {
...state, ...nextState };
}
}
}
pendingStates.length = 0;
return state;
}
}
function shouldUpdate(componentInstance, nextProps, nextState) {
componentInstance.props = nextProps;
componentInstance.state = nextState;
if (
componentInstance.shouldComponentUpdate &&
!componentInstance.shouldComponentUpdate(nextProps, nextState)
) {
return false;
}
componentInstance.forceUpdate();
}
class Component {
constructor(props) {
this.props = props;
this.$updater = new Updater(this);
this.state = {
};
this.nextProps = null;
}
setState(partialState) {
this.$updater.addState(partialState);
}
forceUpdate(){
}
}
Component.prototype.isReactComponent = {
};
export {
Component
}
2. 简单替换DOM
2.1. src/react/component.js
import {
compareTwoElements } from './vdom';
export const updateQueue = {
updaters: [],
isPending: false,
};
class Updater {
constructor(componentInstance) {
this.componentInstance = componentInstance;
this.pendingStates = [];
this.nextProps = null;
}
addState(partialState){
this.pendingStates.push(partialState);
this.emitUpdate();
}
emitUpdate(nextProps){
this.nextProps = nextProps;
if (nextProps || !updateQueue.isPending) {
this.updateComponent();
} else {
updateQueue.add(this);
}
}
updateComponent(){
let {
componentInstance, pendingStates, nextProps } = this;
if (nextProps || pendingStates.length > 0) {
shouldUpdate(componentInstance, nextProps, this.getState());
}
}
getState(){
let {
componentInstance, pendingStates } = this;
let {
state } = componentInstance;
if (pendingStates.length > 0) {
for (let i = 0; i < pendingStates.length; i++) {
let nextState = pendingStates[i];
if (typeof nextState === 'function') {
state = {
...state, ...nextState.call(componentInstance, state) };
} else {
state = {
...state, ...nextState };
}
}
}
pendingStates.length = 0;
return state;
}
}
function shouldUpdate(componentInstance, nextProps, nextState) {
componentInstance.props = nextProps;
componentInstance.state = nextState;
if (
componentInstance.shouldComponentUpdate &&
!componentInstance.shouldComponentUpdate(nextProps, nextState)
) {
return false;
}
componentInstance.forceUpdate();
}
class Component {
constructor(props) {
this.props = props;
this.$updater = new Updater(this);
this.state = {
};
this.nextProps = null;
}
setState(partialState) {
this.$updater.addState(partialState);
}
forceUpdate(){
let {
props, state, renderElement: oldRenderElement } = this;
if (this.componentWillUpdate) {
this.componentWillUpdate();
}
let newRenderElement = this.render();
let currentElement = compareTwoElements(oldRenderElement, newRenderElement);
this.renderElement = currentElement;
if (this.componentDidUpdate) {
this.componentDidUpdate();
}
}
}
Component.prototype.isReactComponent = {
};
export {
Component
}
2.2. src/react/vdom.js
import {
TEXT, ELEMENT, CLASS_COMPONENT, FUNCTION_COMPONENT } from './constants';
import {
setProps, onlyOne, flatten } from './utils';
export function compareTwoElements(oldRenderElement, newRenderElement) {
oldRenderElement = onlyOne(oldRenderElement);
newRenderElement = onlyOne(newRenderElement);
let currentDOM = oldRenderElement.dom;
let currentElement = oldRenderElement;
if (newRenderElement == null) {
currentDOM.parentNode.removeChild(currentDOM);
currentDOM