组件的通信
关于setState的细节
setState(stateChange[, callback])
关于setState要知道两点
- 应该将
setState视为请求而不是更新组件的命令。为了更好的感知性能,React 会延迟调用它,然后通过一次传递更新多个组件。React 并不会保证 state 的变更会立即生效。所以setState是异步的。 setState中传入对象,会进行一个浅合并,相当于Object.assign
观察之后证明了两点:
1、 this.setState是异步的
- 按下同步增加的按钮,可以看到打印出来的state是之前的state,然而视图已经更新了
- 按下异步增加的按钮,可以看到打印出来的state与视图的state相同
2、
this.setState对对象进行了浅合并 - 按下增加的按钮,只修改count,可以看到打印出来的state中,msg的属性均未变化
- 按下错误修改的按钮,只对
msg.id更新,发现msg.title属性不见了 - 按下正确修改的按钮,将
msg整体通过展开操作符将全部属性拷贝过来,再对想要修改的属性进行覆盖,发现msg.title没有消失
import React, { Component } from "react";
class App extends Component {
state = {
count: 0,
msg: {
title: "no-change",
id: 0
}
};
asyncHandleAddCount = async() => {
await this.setState({ count: this.state.count + 1 });
console.log(this.state)
};
handleAddCount = () => {
this.setState({ count: this.state.count + 1 });
console.log(this.state)
};
asyncErrorHandleChange = async() => {
const {msg} = this.state;
await this.setState({ msg: {id:msg.id+1}});
console.log(this.state)
};
asyncTrueHandleChange = async() => {
const {msg} = this.state;
await this.setState({ msg: {...msg,id:msg.id+1}});
console.log(this.state)
};
render() {
const {count, msg} = this.state;
return (
<>
<div>
{count}
<button onClick={this.asyncHandleAddCount}>异步+</button>
<button onClick={this.handleAddCount}>同步+</button>
</div>
<div>
{msg.title}-----{msg.id}
<button onClick={this.asyncErrorHandleChange}>错误更改</button>
<button onClick={this.asyncTrueHandleChange}>正确修改</button>
</div>
</>
);
}
}
export default App;
父组件向子组件之间的通信
- 父组件向子组件的通信通过props进行一个通讯
子组件向父组件之间的通信
- 子组件向父组件通信通过回调函数
import React, { Component } from "react";
class App extends Component {
state = {
count: 0,
};
handleAddCount = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<>
<Counter count={this.state.count} onAddCount={this.handleAddCount} />
</>
);
}
}
// 这里的函数组件中,默认的参数为props:{}
// 在这里我进行了解构,拿到我想要的属性
function Counter({count, onAddCount}) {
return (
<>
<div>{count}</div>
// 子组件通过回调函数,向父组件通信
<button onClick={onAddCount}>+</button>
</>
)
}
export default App;
不同组件之间的通信
- 不同组件之间的通信通过Context
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
如何创建context
import React, { Component } from "react";
const MyContext = React.createContext();
创建一个Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。
嵌套组件如何使用并更新Context中的值
- 首先我们创建好的Context对象MyContext具有两个属性Povider和Consumer。
- Provider 接收一个
value 属性,传递给消费组件。在Provider下的嵌套组件可通过Consumer访问value。 - Consumer需要
函数作为子元素这种做法。这个函数接收当前的 context 值,返回一个React 节点。Parent
class App extends Component {
state = {
count: 0,
};
// 实现一个用context做的计数器
handleAddCount = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
console.log(MyContext);
return (
<>
// 提供value给后续的嵌套组件
<MyContext.Provider
value={{ count: this.state.count, add: this.handleAddCount }}
>
<Son />
</MyContext.Provider>
</>
);
}
}
Son
function Son() {
return (
<>
// 中间层并没有实际意义
I m Son <br />
<FunctionGrandson />
</>
);
}
FunctionGrandson
function FunctionGrandson() {
return (
<>
<MyContext.Consumer>
// consumer需要用函数的形式返回一个子节点
{(context) => {
console.log(context);
return (
<>
<div>I m FunctionGrandson</div>
{context.count}
<button onClick={context.add}>change</button>
</>
);
}}
</MyContext.Consumer>
</>
);
}
Result:
- 另外,除了使用Consumer,使用类组件形式的组件还可以通过设置contextType,然后通过context属性来访问。
挂载在 class 上的
contextType 属性会被重赋值为一个由 React.createContext() 创建的Context 对象。这能让你使用 this.context 来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它,包括 render 函数中。
class ClassGrandson extends Component {
static contextType = MyContext;
render() {
console.log(this);
return (
<>
<div>I m ClassGrandson</div>
{this.context.count}
<button onClick={this.context.add}>+</button>
</>
);
}
}
组件的生命周期
分为三个阶段,挂载,更新以及卸载
挂载mount,从初始化组件 -> 组件构建的视图 -> 渲染到真实dom中
- constructor(){} --实例化组件调用
- static getDerivedStateFromProps(props){} --将props关联到state
- render(){} --构建视图
- componentDidMount(){} --已经渲染到真实dom
import React, { Component } from "react";
class App extends Component {
// 1==实例化
constructor() {
super()
this.state = {
count: 0
}
console.log("constructor-app")
}
// 7==渲染到真实dom
componentDidMount(){
console.log("componentDidMount-app")
}
// 2==构建虚拟dom
render() {
console.log("render-app")
return (
<>
<Counter count={this.state.count}/>
</>
);
}
}
class Counter extends Component {
// 3==
constructor(){
super()
this.state = {
title: "Counter"
}
console.log("constructor-counter")
}
// 6==
componentDidMount(){
console.log("componentDidMount-counter")
}
// 4==
static getDerivedStateFromProps(props){
console.log("getDerivedStateFromProps-counter")
return props;
}
// 5==
render() {
const {count} = this.props;
console.log("render-counter")
console.log(this.state)
return (
<>
<div>{count}</div>
</>
);
}
}
export default App;
更新update, 从组件开始更新 -> 更新结束
- static getDerivedStateFromProps(props,state){}
- shouldComponentUpdate(nextProps, nextState){} -- 判断是否更新
- render(){}
- getSnapshotBeforeUpdate(){}
- componentDidUpdate(){} -- 处理副作用(请求)
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = {
count: 0,
};
}
handleAddCount = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<>
<Counter count={this.state.count} onAddCount={this.handleAddCount} />
</>
);
}
}
class Counter extends Component {
constructor() {
super();
this.state = {
title: "Counter",
};
}
// 1==
static getDerivedStateFromProps(props) {
console.log("getDerivedStateFromProps-counter");
return props;
}
// 2==
shouldComponentUpdate(nextProps, nextState) {
console.log(nextProps, nextState);
// 必须要有返回值,true为继续更新,false打断更新
return true;
}
// 5==
componentDidUpdate(prevProps, prevState, prevDOM) {
console.log(prevDOM);
}
// 4==
getSnapshotBeforeUpdate(prevProps, prevState) {
// 要用这个函数必须要有componentDidUpdate
// 获取准备更新前的真实DOM快照, 用于获取更新前的DOM快照
// DOM快照返回给componentDidUpdate
return document.querySelector("#count");
}
// 3==
render() {
const { count } = this.props;
console.log("render-counter");
console.log(this.state);
return (
<>
<div id="count">{count}</div>
<button onClick={this.props.onAddCount}>+</button>
</>
);
}
}
export default App;
卸载阶段
- componentWillUnmount -> 组件卸载之前清除掉副作用(定时器,事件监听等)
举个例子,我们在组件挂载完毕后给window加上一个resize的事件监听,如果我们把组件卸载之后会发生什么?
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = {
show: true,
};
}
handleToggleShow = () => {
this.setState({ show: !this.state.show });
};
render() {
return (
<>
{this.state.show && <Counter />}
<button onClick={this.handleToggleShow}>toggle</button>
</>
);
}
}
class Counter extends Component {
constructor() {
super();
this.state = {
height: window.innerHeight,
};
}
showWindowWidth = () => {
this.setState({ height: window.innerHeight });
};
// 给window加上监听函数
componentDidMount() {
window.onresize = this.showWindowWidth;
}
render() {
return <>{this.state.height}</>;
}
}
export default App;
组件卸载之后,组件确实销毁了。可是onresize中的处理函数还存着this.setState没被处理。所以我们需要加上componentWillUnmount
class Counter extends Component {
constructor() {
super();
this.state = {
height: window.innerHeight,
};
}
showWindowWidth = () => {
this.setState({ height: window.innerHeight });
};
componentDidMount() {
window.onresize = this.showWindowWidth;
}
// 卸载前将监听器去除
componentWillUnmount() {
window.onresize = null;
}
render() {
return <>{this.state.height}</>;
}
}