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;
}
相关推荐
木斯佳2 小时前
前端八股文面经大全:快手前端一面 (2026-04-07)·面经深度解析
前端·ai·性能优化·hooks·移动端适配
小陈工2 小时前
Python Web开发入门(十三):API版本管理与兼容性——让你的接口优雅地“长大”
开发语言·前端·人工智能·python·安全·oracle
李宏伟~2 小时前
大文件分片案例html + nodejs + 视频上传案例
javascript·html·音视频
焦糖玛奇朵婷3 小时前
盲盒小程序开发,盲盒小程序怎么做
java·大数据·服务器·前端·小程序
豆苗学前端3 小时前
技术复盘文档:HTTPS 站点安全下载 HTTP 资源实践总结
前端
南囝coding3 小时前
Claude Code 多 Agent 协作:Subagents 和 Agent Teams 怎么选?
前端·后端
史迪仔01123 小时前
[QML] QT5和QT6 圆角的不同设置方法
前端·javascript·qt
Z_Wonderful3 小时前
React react-app-env.d.ts是 TypeScript 的全局类型声明文件,它的作用
前端·react.js·typescript
码云之上3 小时前
从 SQL DDL 到 ER 图:前端如何优雅地实现数据库可视化
前端·数据库·数据可视化