React setState模拟实现

这篇博客深入解析了React中setState的异步性质和批量更新机制。在合成事件和钩子函数中,setState表现为异步,实际上在原生事件和setTimeout中则是同步的。批量更新仅在特定场景下发生,例如在合成事件和钩子函数中,多次setState会被合并处理,取最后一次的值。文中还通过示例代码展示了如何手动实现批量更新的策略。
摘要由CSDN通过智能技术生成

setState实现
setState只在合成事件和钩子函数中是“异步”的,在原生事件和setTimeout 中都是同步的。
setState 的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。
setState 的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次setState,setState的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时setState多个不同的值,在更新时会对其进行合并批量更新。

let batchingStrategy = {
  // 默认是非批量更新模式
  isBatchingUpdates: false,
  // 脏组件 组件的状态改变了
  dirtyComponents: [],
  // 批量更新的方法
  batchedUpdates() {},
};

class Transaction {
  constructor(wraps) {
    this.wraps = wraps || [];
  }

  perform(fn) {
    this.wraps.forEach((wrap) => wrap.initialize());
    fn.call();
    this.wraps.forEach((wrap) => wrap.close());
  }
}

let transaction = new Transaction([
  {
    initialize() {
      // 开启批量更新模式
      batchingStrategy.isBatchingUpdates = true;
    },
    close() {
      // 关闭批量更新模式
      batchingStrategy.isBatchingUpdates = false;
      // 将脏组件全部更新
      batchingStrategy.dirtyComponents.forEach((component) =>
        component.updateComponent()
      );
    },
  },
]);

window.trigger = function (event, method) {
  let component = event.target.component;
  transaction.perform(component[method].bind(component));
};

class Updater {
  constructor(component) {
    this.component = component;
    this.pendingStates = [];
  }

  addState(partcialState) {
    this.pendingStates.push(partcialState);
    // 判读是否批量更新,是批量更新,就先把组件缓存起来
    // 不是批量更新,直接更新
    batchingStrategy.isBatchingUpdates
      ? batchingStrategy.dirtyComponents.push(this.component)
      : this.component.updateComponent();
  }
}

class Component {
  constructor(props) {
    this.props = props;
    this.$updater = new Updater(this);
  }

  renderElement() {
    let htmlStr = this.render();
    let div = document.createElement("div");
    div.innerHTML = htmlStr;
    this.domElement = div.children[0];
    // 将组件实例挂到dom元素上
    this.domElement.component = this;
    return this.domElement;
  }

  mount(container) {
    container.appendChild(this.renderElement());
  }

  setState(partcialState) {
    this.$updater.addState(partcialState);
  }

  updateComponent() {
    this.$updater.pendingStates.forEach((partcialState) =>
      Object.assign(this.state, partcialState)
    );
    this.$updater.pendingStates.length = 0;
    let oldElement = this.domElement;
    let newElement = this.renderElement();
    oldElement.parentElement.replaceChild(newElement, oldElement);
  }
}

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      number: 0,
    };
  }

  add() {
    this.setState({
      number: this.state.number + 1,
    });
    console.log(this.state); //0
    this.setState({
      number: this.state.number + 1,
    });
    console.log(this.state); //0

    setTimeout((_) => {
      this.setState({
        number: this.state.number + 1,
      });
      console.log(this.state); //3

      this.setState({
        number: this.state.number + 1,
      });
      console.log(this.state); //4
    });
  }

  render() {
    return `<button οnclick="trigger(event,'add')" >${this.props.name}:${this.state.number}</button>`;
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值