使用EventBus进行非父子组件通信
安装依赖
npm i hy-event-store
基本使用:
孙子组件触发事件
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>
<span>{age}</span>
</div>
<Home />
</div>
);
}
}
export default App;
为什么使用setState
- 开发中我们不能直接通过修改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;
获取异步的结果
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;