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 分钟前
并查集:基础认识与模拟实现
android·java·javascript·数据结构·c++·算法·并查集
电商API_1800790524710 分钟前
电商商品价格监控工具淘宝京东商品价格抓取API项目实操分享
java·大数据·开发语言·前端·数据挖掘
sxmas21 分钟前
用状态机与DAG建模四阶段业务流程:以MIND模型为例
前端
八角丶1 小时前
Node 网络编程 —— TLS 模块
javascript·后端·node.js
倔强的石头1061 小时前
【Linux指南】动静态库系列(七):符号表与重定位:链接器如何把函数调用接起来
linux·前端·javascript
图扑软件2 小时前
中篇・运笔|统一 DataModel 底座,HT UI 组件万物同源
javascript·低代码·ui·性能优化·数据可视化
a1117762 小时前
3D 脑图谱 / Bilingual 3D Brain Atlas 项目
前端
T1mzhou2 小时前
ARM64 Linux 6.10内核启动流程2-rootfs 怎么挂上
linux·服务器·前端
小小尚@3 小时前
WebGIS/ECharts 地图开发|MapLand 在线行政区划边界一键下载 GeoJSON 工具
前端·javascript·echarts
葡萄城技术团队3 小时前
工业数据可视化:使用活字格 AIcoding + Three.js 构建轻量化三维设备监控大屏
开发语言·javascript·信息可视化