在React中使用EventBus进行非父子组件通信,为什么使用setState,setState详细使用

332 阅读3分钟

使用EventBus进行非父子组件通信

安装依赖 npm i hy-event-store

image.png 基本使用: 孙子组件触发事件

import React, { Component } from "react";
import eventBus from "./utils/event-bus";

export class HomeBanner extends Component {
  prevClick() {
    eventBus.emit("prev", "zm", 21);
  }
  nextClick() {
    eventBus.emit("next", "wzm", 3);
  }
  render() {
    return (
      <div>
        <h1>HomeBanner</h1>
        <div>
          <button onClick={() => this.prevClick()}>prev</button>
          <button onClick={() => this.nextClick()}>next</button>
        </div>
      </div>
    );
  }
}
export default HomeBanner;

爷爷组件监听

import React, { Component } from "react";
import Home from "./Home";
import eventBus from "./utils/event-bus";

export class App extends Component {
  constructor() {
    super();
    this.state = {
      name: "default_name",
      age: 0,
    };
  }
  componentDidMount() {
    eventBus.on("prev", this.eventPrevClickHandle.bind(this));
    eventBus.on("next", this.eventNextClickHandle.bind(this));
  }
  eventPrevClickHandle(name, age) {
    console.log("App中监听到prev  name,age=> ", name, age);
    this.setState({
      name,
      age,
    });
    // console.log("this=> ", this);
  }
  eventNextClickHandle(name, age) {
    console.log("App中监听到next  name,age=> ", name, age);
    this.setState({
      name,
      age,
    });
    // console.log("this=> ", this);
  }
  componentWillUnmount() {
    eventBus.off("prev", this.eventPrevClickHandle);
    eventBus.off("next", this.eventNextClickHandle);
  }
  render() {
    const { name, age } = this.state;
    return (
      <div>
        <h1>App</h1>
        <div>
          <span>{name}</span>&nbsp;&nbsp;
          <span>{age}</span>
        </div>
        <Home />
      </div>
    );
  }
}
export default App;

为什么使用setState

image.png

  • 开发中我们不能直接通过修改state的值来让界面发生更新:就算我们修改state,但是这种直接修改state的值的方式,React并不知道数据发生了变化
  • React中并没有实现类似于Vue2中的Object.defineProperty或者Vue3中的Proxy的方式来监听数据的变化
  • 必须通过setState来告知React,数据已经发生了变化
  • setState方法是从Component中继承过来的

setState几种写法及setState异步更新

为什么setState设计成异步更新?

  • 显著的性能提升
  • 若每次调用setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染,效率低
  • 最好的办法应该是获取到多个更新,之后进行批量更新
  • 如果同步更新了state,但是还没有执行render函数,那么state和props不能保持同步
  • state和props不能保持一致性,会在开发中产生很多问题
import React, { Component } from "react";

export class App extends Component {
  constructor() {
    super();
    this.state = {
      msg: "hello zm",
      count: 0,
    };
  }
  changeText() {
    // 1setState传对象
    // this.setState({
    //   msg: "hhh",
    // });

    // 2.setState可以传入一个回调函数
    // 可以在回调函数中编写新的state的逻辑
    // 当前的回调函数会将之前的state和props传递进来
    // this.setState((state, props) => {
    //   // 1.编写一些对新的state处理逻辑
    //   // 2.可以获取之前的state和props值
    //   console.log("this.state.msg,this.props=> ", this.state.msg, this.props); //App.jsx中root.render(<App title="ww" />);
    //   return {
    //     msg: "666",
    //   };
    // });

    // 3.setState在React的事件处理中是一个异步调用
    // 如果希望在数据更新之后(Object.assign()数据合并),获取到对应的结果执行一些逻辑代码
    // 那么可以在setState中传入第二个参数:callback
    this.setState({ msg: "999" }, () => {
      console.log("后执行 this.state.msg=> ", this.state.msg);
    });
    console.log("先执行 ", this.state.msg);

    // 先执行  hello zm
    // componentDidUpdate  999
    // 后执行 this.state.msg=>  999
  }
  componentDidUpdate() {
    console.log("componentDidUpdate ", this.state.msg);
  }
  add() {}
  render() {
    const { msg, count } = this.state;
    return (
      <div>
        <h1>{msg}</h1>
        <h1>{count}</h1>
        <button onClick={() => this.changeText()}>changeText</button>
        <button onClick={() => this.add()}>+</button>
      </div>
    );
  }
}
export default App;

获取异步的结果

image.png

setState是同步还是异步

React18之前:

  • 在组件生命周期或React合成事件中,setState是异步
  • 在setTimeout或者原生dom事件中,setState是同步

React18之后,setTimeout中setState是异步操作(批处理)

import React, { Component } from "react";
import { flushSync } from "react-dom";

export class App extends Component {
  constructor() {
    super();
    this.state = {
      msg: "hello zm",
      count: 0,
    };
  }
  changeText() {
    setTimeout(() => {
      // 在react18之前,setTimeout中setState操作是同步操作
      // 在react18之后,setTimeout中setState是异步操作(批处理)
      // this.setState({ msg: "async!" });
      // console.log(this.state.msg);

      // 如果希望代码可以同步(打印msg在render之后),则需要执行特殊的flushSync操作(import { flushSync } from 'react-dom')
      flushSync(() => {
        this.setState({ msg: "sync!" });
      });
      console.log(this.state.msg);
    }, 0);
  }
  componentDidUpdate() {
    console.log("componentDidUpdate ", this.state.msg);
  }
  add() {}
  render() {
    const { msg, count } = this.state;
    return (
      <div>
        <h1>{msg}</h1>
        <h1>{count}</h1>
        <button onClick={() => this.changeText()}>changeText</button>
        <button onClick={() => this.add()}>+</button>
      </div>
    );
  }
}
export default App;