【React源碼】Day02——實現事件綁定和同步狀態更新

【React源碼】Day02——實現事件綁定和同步狀態更新

實現事件綁定

githubreact

// react-dom.js
function updateDOMAttr(dom, props) {
  for (let key in props) {
    if (key === "children") {
      continue;
    }
    if (key === "style") {
      for (let attr in props[key]) {
        dom.style[attr] = props[key][attr];
      }
    } else if (key.startsWith("on")) {
      // onClick => onclick
      dom[key.toLocaleLowerCase()] = newProps[key]
    } else {
      dom[key] = props[key];
    }
  }
}

實現 setState()

同步更新

githubgit

  • 當調用 this.setState() 時,實際調用 Component 類的 setState
  • 建立 Updater 類
    • 用於收集狀態 addState(),this.pendingStates = []
    • 合併狀態,getState()
    • 觸發視圖進行強制刷新,updateComponent()
  • Component 類中 forceUpdate() 強制更新視圖,將虛擬 dom 轉化爲真實 dom,掛載到視圖
// Component.js
class Updater {
  constructor(classInstance) {
    // 類組件實例
    this.classInstance = classInstance;
    // 收集狀態
    this.pendingStates = [];
  }

  addState(partialState) {
    this.pendingStates.push(partialState);
    // 若是當前處於批量更新模式
    updateQueen.isBatchingUpdate
      ? updateQueen.add(this)
      : this.updateComponent();
  }

  updateComponent() {
    let { classInstance } = this;
    if (this.pendingStates.length > 0) {
      // 替換 state 爲最新的狀態
      classInstance.state = this.getState();
      // 強制視圖進行更新
      classInstance.forceUpdate();
    }
  }
  // 獲取最新的狀態
  getState() {
    let { classInstance, pendingStates } = this;
    let { state } = classInstance;
    if (pendingStates.length > 0) {
      pendingStates.forEach((nextState) => {
        if (isFunction(nextState)) {
          nextState = nextState(state);
        } else {
          state = { ...state, ...nextState };
        }
      });
      pendingStates.length = 0;
    }
    return state;
  }
}
// Component.js
class Component {
  static isReactComponent = true;
  constructor(props) {
    this.props = props;
    this.state = {};

    this.updater = new Updater(this);
  }

  setState(partialState) {
    this.updater.addState(partialState);
  }

  // 更新視圖
  forceUpdate() {
    // 此時 renderVdom 已是更新後的虛擬 DOM(state已經更新)
    let renderVdom = this.render();
    updateClassComponent(this, renderVdom);
  }
}

function updateClassComponent(classInstance, renderVdom) {
  let oldDOM = classInstance.dom;
  let newDOM = createDOM(renderVdom);
  oldDOM.parentNode.replaceChild(newDOM, oldDOM);
  classInstance.dom = newDOM;
}

異步更新

githubgithub

  • 添加隊列函數 updateQueen 使用 add() 收集 updaters 並使用 batchUpdate() 更新(相似發佈訂閱)dom

  • 修改 index.js 文件內容,手動改變 isBatchingUpdate 的值,並手動清空隊列更新(這裏有點 low )異步

// index.js
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { number: 0 };
  }
  handleClick = () => {
    updateQueen.isBatchingUpdate = true;
    this.setState({ number: this.state.number + 1 });
    console.log(this.state);

    setTimeout(() => {
      updateQueen.batchUpdate();
    });
  };
  render() {
    return (
      <div>
        <p>number:{this.state.number}</p>
        <button onClick={this.handleClick}>點擊</button>
      </div>
    );
  }
}
export const updateQueen = {
  updaters: [],
  isBatchingUpdate: false,
  add(updater) {
    this.updaters.push(updater);
  },
  batchUpdate() {
    this.updaters.forEach((updater) => updater.updateComponent());
    this.isBatchingUpdate = true;
  },
};
相關文章
相關標籤/搜索