React - 组件优化、children props 与 render props、错误边界

一、组件优化

1、问题引入
(1)基本介绍
  1. 只要执行 setState,即使不改变状态数据, 组件也会重新 render

  2. 只要当前组件重新 render,就会自动重新 render 子组件,纵使子组件没有用到父组件的任何数据

  3. 只要父组件更新,不管子组件的 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;
}
  1. 首次渲染,输出结果如下

    Parent 渲染了
    Child 渲染了

  2. 第 1 次点击【点我修改】按钮,输出结果如下

    Parent 渲染了
    Child 渲染了

  3. 第 2 次点击【点我修改】按钮,输出结果如下

    Parent 渲染了
    Child 渲染了

2、问题原因
  • Component 中的 shouldComponentUpdate 方法总是返回 true
3、处理策略
  1. 重写 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>
        );
    }
}
  1. 使用 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)基本介绍
  1. PureComponent 只是进行 state 和 props 数据的浅比较,如果是数据对象内部数据变了,返回 false

  2. 不要直接修改 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;
}
  1. 首次渲染,输出结果如下

    Parent 渲染了
    Child 渲染了

  2. 第 1 次点击【点我修改 1】按钮,输出结果如下

    Parent 渲染了
    Child 渲染了

  3. 第 2 次点击【点我修改 2】按钮,输出结果如下

    Parent 渲染了
    Child 渲染了

  4. 第 1 次点击【点我修改 2】按钮,输出结果如下

    (无)

  5. 第 2 次点击【点我修改 2】按钮,输出结果如下

    (无)


二、children props 与 render props

1、基本介绍
  1. children props 是通过组件标签体的方式向组件内部传递内容

  2. 在组件内部,可以通过 props.children 来获取这些内容

  3. 但是,如果内容需要父组件内部的数据,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、基本介绍
  1. 错误边界(Error Boundary)用来捕获后代组件错误,渲染出备用页面

  2. 只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误,以及其他组件在合成事件、定时器中产生的错误

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;
}
相关推荐
程序员黑豆6 小时前
Java类型推断完全指南:从var到菱形运算符,掌握使用限制与最佳实践
java·前端·ai编程
To_OC7 小时前
踩了个 TS 的坑之后,我终于把 type 和 interface 掰明白了
前端·react.js·typescript
GreenTea7 小时前
深度解读 Anthropic 多智能体报告:更强的模型 ≠ 更好的协调
前端·后端·算法
浮生望8 小时前
前端API工程化:用 Mock 数据与 Axios 配置实现独立于后端的并行开发
前端
万少8 小时前
给 DeepSeek Harness 装个"应用商店":一条命令,595 个插件随你逛
前端·javascript·后端
波波0078 小时前
C# 15 重磅新特性: 带标签 break 与 continue:重新定义嵌套循环控制流
服务器·前端·c#
Brown.alexis9 小时前
es6知识点1-自备使用
前端·ecmascript·es6
記億揺晃着的那天10 小时前
NAS 内网域名访问为什么需要浏览器授权
网络·nginx·js·nas
小爬的老粉丝10 小时前
JavaScript 纯前端预览 WPS:先识别容器,再路由解析器
前端·javascript·wps
计算机魔术师10 小时前
新兴多智能体系统的模式与问题
前端