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;
}
相关推荐
QQ1__8115175155 小时前
Spring boot名城小区物业管理系统信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】
前端·vue.js·spring boot
钛态5 小时前
前端微前端架构:大项目的救命稻草还是自找麻烦?
前端·vue·react·web
一粒黑子5 小时前
【实战解析】阿里开源 PageAgent:纯前端 GUI Agent,一行JS让网页支持自然语言操控
前端·javascript·开源
独角鲸网络安全实验室5 小时前
2026微信小程序抓包全解析:从实操落地到合规风控,解锁前端调试新范式
前端·微信小程序·小程序·抓包·系统代理绕过·https证书严格校验·进程隔离
紫微AI5 小时前
前端文本测量成了卡死一切创新的最后瓶颈,pretext实现突破了
前端·人工智能·typescript
GISer_Jing5 小时前
AI前端(From豆包)
前端·aigc·ai编程
IT枫斗者5 小时前
前端部署后如何判断“页面是不是最新”?一套可落地的版本检测方案(适配 Vite/Vue/React/任意 SPA)
前端·javascript·vue.js·react.js·架构·bug
测试修炼手册5 小时前
[测试技术] 深入理解 JSON Web Token (JWT)
前端·json
AI老李6 小时前
2026 年 Web 前端开发的 8 个趋势!
前端
里欧跑得慢6 小时前
15. Web可访问性最佳实践:让每个用户都能平等访问
前端·css·flutter·web