一、组件优化
1、问题引入
(1)基本介绍
-
只要执行 setState,即使不改变状态数据, 组件也会重新 render
-
只要当前组件重新 render,就会自动重新 render 子组件,纵使子组件没有用到父组件的任何数据
-
只要父组件更新,不管子组件的 props 变没变,子组件都会重新渲染
(2)演示
jsx
import { Component } from "react";
import Child from "../Child";
import "./index.css";
export class Parent extends Component {
state = { username: "tom", age: 18 };
change = () => {
this.setState({ username: this.state.username, age: this.state.age });
};
render() {
console.log("Parent 渲染了");
const { username, age } = this.state;
return (
<div className="parent">
<h3>Parent</h3>
<div>username: {username}</div>
<div>age: {age}</div>
<button onClick={this.change}>点我修改</button>
<Child />
</div>
);
}
}
css
.parent {
width: 500px;
background-color: orange;
padding: 8px;
}
jsx
import { Component } from "react";
import "./index.css";
export default class Child extends Component {
render() {
console.log("Child 渲染了");
return (
<div className="child">
<h3>Child</h3>
</div>
);
}
}
css
.child {
width: 100%;
background-color: skyblue;
padding: 8px;
}
-
首次渲染,输出结果如下
Parent 渲染了
Child 渲染了 -
第 1 次点击【点我修改】按钮,输出结果如下
Parent 渲染了
Child 渲染了 -
第 2 次点击【点我修改】按钮,输出结果如下
Parent 渲染了
Child 渲染了
2、问题原因
- Component 中的 shouldComponentUpdate 方法总是返回 true
3、处理策略
- 重写 shouldComponentUpdate 方法,比较新旧 state 或 props 数据,如果有变化则返回 true,没有则返回 false
jsx
import { Component } from "react";
import Child from "../Child";
import "./index.css";
export class Parent extends Component {
state = { username: "tom", age: 18 };
change = () => {
this.setState({ username: this.state.username });
};
shouldComponentUpdate(nextProps, nextState) {
// nextProps 是接下来要变化的目标 props
// nextState 是接下来要变化的目标 state
return nextState.username !== this.state.username || nextState.age !== this.state.age;
}
render() {
console.log("Parent 渲染了");
const { username, age } = this.state;
return (
<div className="parent">
<h3>Parent</h3>
<div>username: {username}</div>
<div>age: {age}</div>
<button onClick={this.change}>点我修改</button>
<Child />
</div>
);
}
}
- 使用 PureComponent,它重写了 shouldComponentUpdate 方法,只有 state 或 props 数据有变化才返回 true
jsx
import { PureComponent } from "react";
import Child from "../Child";
import "./index.css";
export class Parent extends PureComponent {
state = { username: "tom", age: 18 };
change = () => {
this.setState({ username: this.state.username });
};
render() {
console.log("Parent 渲染了");
const { username, age } = this.state;
return (
<div className="parent">
<h3>Parent</h3>
<div>username: {username}</div>
<div>age: {age}</div>
<button onClick={this.change}>点我修改</button>
<Child />
</div>
);
}
}
4、注意事项
(1)基本介绍
-
PureComponent 只是进行 state 和 props 数据的浅比较,如果是数据对象内部数据变了,返回 false
-
不要直接修改 state 数据,而是要产生新数据
(2)演示
jsx
import { PureComponent } from "react";
import Child from "../Child";
import "./index.css";
export class Parent extends PureComponent {
state = {
person: {
username: "tom",
age: 18,
},
};
// 这里改变了第 1 层
change1 = () => {
this.setState({ person: { ...this.state.person } });
};
// 这里改变了第 2 层,没有改变第 1 层
change2 = () => {
const { person } = this.state;
person.username = "jerry";
person.age = 20;
this.setState({ person: person });
};
render() {
console.log("Parent 渲染了");
const { username, age } = this.state.person;
return (
<div className="parent">
<h3>Parent</h3>
<div>username: {username}</div>
<div>age: {age}</div>
<button onClick={this.change1}>点我修改 1</button>
<button onClick={this.change2}>点我修改 2</button>
<Child />
</div>
);
}
}
css
.parent {
width: 500px;
background-color: orange;
padding: 8px;
}
jsx
import { Component } from "react";
import "./index.css";
export default class Child extends Component {
render() {
console.log("Child 渲染了");
return (
<div className="child">
<h3>Child</h3>
</div>
);
}
}
css
.child {
width: 100%;
background-color: skyblue;
padding: 8px;
}
-
首次渲染,输出结果如下
Parent 渲染了
Child 渲染了 -
第 1 次点击【点我修改 1】按钮,输出结果如下
Parent 渲染了
Child 渲染了 -
第 2 次点击【点我修改 2】按钮,输出结果如下
Parent 渲染了
Child 渲染了 -
第 1 次点击【点我修改 2】按钮,输出结果如下
(无)
-
第 2 次点击【点我修改 2】按钮,输出结果如下
(无)
二、children props 与 render props
1、基本介绍
-
children props 是通过组件标签体的方式向组件内部传递内容
-
在组件内部,可以通过
props.children来获取这些内容 -
但是,如果内容需要父组件内部的数据,children props 做不到,需要使用 render props 来实现
2、演示
(1)children props
jsx
export default function Card(props) {
return (
<>
<h3>Card</h3>
{props.children}
</>
);
}
jsx
import Card from "./components/Card";
export default function App() {
return (
<>
<Card>
<p>这是卡片内部的内容</p>
<button>点击按钮</button>
</Card>
</>
);
}
(2)render props
jsx
import { useState } from "react";
export default function Card(props) {
const [content] = useState("这是一段测试内容");
return (
<>
<h3>Card</h3>
{props.render(content)}
</>
);
}
jsx
export default function CardInner(props) {
return (
<>
<p>这是卡片内部的内容</p>
<button>点击按钮</button>
<div>{props.content}</div>
</>
);
}
jsx
import Card from "./components/Card";
import CardInner from "./components/CardInner";
export default function App() {
return (
<>
<Card render={(content) => <CardInner content={content} />} />
</>
);
}
三、错误边界
1、基本介绍
-
错误边界(Error Boundary)用来捕获后代组件错误,渲染出备用页面
-
只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误,以及其他组件在合成事件、定时器中产生的错误
2、演示
jsx
import { PureComponent } from "react";
import Child from "../Child";
import "./index.css";
export class Parent extends PureComponent {
state = {
hasError: "",
};
static getDerivedStateFromError(error) {
console.log("@@@", error);
return { hasError: error };
}
componentDidCatch(error, info) {
console.log("此处统计错误,反馈给服务");
console.log(error, info);
}
render() {
return (
<div className="parent">
<h3>Parent</h3>
{this.state.hasError ? <h3>当前网络不稳定,稍后再试</h3> : <Child />}
</div>
);
}
}
css
.parent {
width: 500px;
background-color: orange;
padding: 8px;
}
jsx
import { Component } from "react";
import "./index.css";
export default class Child extends Component {
// state = {
// users: [
// { id: "001", name: "tom", age: 18 },
// { id: "002", name: "jack", age: 19 },
// { id: "003", name: "merry", age: 20 },
// ],
// };
// 模拟一个错误
state = {
users: "test",
};
render() {
return (
<div className="child">
<h3>Child</h3>
{this.state.users.map((userObj) => {
return (
<div key={userObj.id}>
{userObj.name} ----- {userObj.age}
</div>
);
})}
</div>
);
}
}
css
.child {
width: 100%;
background-color: skyblue;
padding: 8px;
}