一场组件的进化脱口秀——React从 “类” 到 “hooks” 的 “改头换面”

前言

家人们,咱就是说,React 这玩意儿就纯纯一个 "互联网打工人" ,几年不更新,直接从 "穿西装打领带的老派白领"(类组件) ,进化成了 "穿卫衣踩拖鞋的高效新青年"(函数组件 + hooks) 。但不得不说是真好用!看完你就知道这波 "改头换面" 到底有多爽。

一、React 16.8 前:类组件的 "老派职场生存法则"

hooks 还没出生的年代,写 React 组件那叫一个 "仪式感拉满" ------ 必须套个class,像入职要填一堆表格似的。总之一个字--"装"

1. 状态,得用this.state"供着"?

想存个变量还能让组件 "动起来"?得搁constructor里写this.state = {},仿佛给变量办了张 "职场工牌" ,只有挂上这牌,修改它才能触发 "全组开会"(组件重新渲染)

比如:

jsx 复制代码
export default class App extends Component {
    constructor() {
        super();
        this.state = { 
            count: 0 
        } // 给count发工牌:"你是咱组的状态人了!"
    }
    add() {
        this.setState({ 
            count: this.state.count + 1 
        }) // 修改状态="给工牌升级",触发渲染
    }
    render() {
        console.log('render'); // 一修改就"开会"
        return <button onClick={this.add.bind(this)}>{this.state.count}</button>
    }
}

点击前:

点击3次后:

bind(this)更是 "老派痛点" ------ 不用它,this能给你跑到 "外星",主打一个 "我认识你,但你不认识我" 的尴尬。

2. 生命周期:像职场的 "上下班打卡 + 加班预警"

类组件的生命周期,那就是 "打工人的一天":

  • componentDidMount:组件 "入职第一天",刚渲染完就触发,适合干 "刚入职先装个软件"(比如发请求);
  • componentDidUpdate:组件 "每次改需求",状态变了就触发,相当于 "改完方案得同步给全组";
  • componentWillUnmount:组件 "离职前",销毁前触发,用来 "删软件清数据"(比如清定时器)。

依旧代码:

jsx 复制代码
import React, { Component } from 'react';

export default class App3 extends Component {
    constructor(props) {
        super(props);
        // 初始化状态:模拟"入职时的工作清单"和"待办数量"
        this.state = {
            workList: [], // 工作清单
            todoCount: 0  // 待办数量
        };
        // 模拟一个"上班期间的定时提醒"
        this.timer = null;
    }

    // 1. componentDidMount:组件"入职第一天"
    // 刚渲染完成(办完入职手续)就触发,只执行一次!
    // 适合做"入职首件事":比如对接接口拿数据、初始化定时器、绑定事件
    componentDidMount() {
        console.log('✨ 组件入职报到!');
        // 模拟"入职先拉取工作清单"(发请求)
        fetch('https://mock-api.com/work/list')
        .then(res => res.json())
        .then(data => {
                this.setState({
                workList: data.list,
                todoCount: data.list.length
            });
        });
        // 模拟"入职后设置定时提醒"(比如每小时检查待办)
        this.timer = setInterval(() => {
            console.log('⏰ 定时检查:当前待办数 →', this.state.todoCount);
        }, 3600000);
    }

    // 2. componentDidUpdate:组件"每次改需求"
    // 状态/属性变化后(改了工作方案)触发,每次更新都会执行!
    // 适合做"需求变更后的同步操作":比如待办数变了,同步更新统计
    componentDidUpdate(prevProps, prevState) {
        // 注意!一定要加判断,否则会无限循环(改状态→触发更新→又改状态→再更新)
        if (prevState.todoCount !== this.state.todoCount) {
            console.log('📝 需求变更!待办数从', prevState.todoCount, '变成', this.state.todoCount);
            // 模拟"待办数变了,同步到公司看板"
            console.log('🔄 已同步待办数到公司看板~');
        }
    }

    // 3. componentWillUnmount:组件"离职前"
    // 组件销毁(离职)前触发,只执行一次!
    // 适合做"离职收尾工作":清定时器、解绑事件、取消请求,避免内存泄漏
    componentWillUnmount() {
        console.log('👋 组件准备离职!');
        // 清除定时提醒(带走自己的东西,不占公司资源)
        clearInterval(this.timer);
        // 模拟"取消未完成的请求"(避免离职后还发请求打扰公司)
        this.cancelRequest && this.cancelRequest();
        console.log('✅ 收尾工作完成,可安心离职~');
    }

    // 模拟"新增待办"(触发状态更新,进而触发componentDidUpdate)
    addTodo = () => {
        this.setState(prevState => ({
            todoCount: prevState.todoCount + 1
        }));
    };

    render() {
        const { workList, todoCount } = this.state;
        return (
            <div className="work-container">
                <h3>打工人的工作面板</h3>
                <p>当前待办数:{todoCount}</p>
                <button onClick={this.addTodo}>新增待办(改需求)</button>
                <ul>
                    {workList.map((item, index) => (
                        <li key={index}>{item}</li>
                    ))}
                </ul>
            </div>
        );
    }
}

拆解:

1.componentDidMount(入职报到) :组件第一次渲染到页面后,这个方法就像你第一天入职 ------ 办完手续坐在工位上,第一件事肯定是 "对接工作"(发请求拿数据)"配置工作环境"(设定时器)。它只执行一次,不会因为后续改需求重复触发,完美契合 "入职首件事" 的场景。如果在这里忘了设定时器 / 绑事件,后续想补就只能塞到其他地方,容易乱。

2.componentDidUpdate(需求变更) :每次调用setState修改状态(比如点击 "新增待办"),组件重新渲染后就会触发这个方法,像极了公司改需求:你改完方案后,得同步给产品、测试、后端(对应代码里 "同步待办数到看板")。但一定要加prevState/prevProps的判断!不然每次更新都改状态,会陷入 "改需求→同步→又改需求→又同步" 的无限循环,就像打工人改需求改到崩溃。

3.componentWillUnmount(离职收尾) :当组件从页面消失(比如路由跳转、关闭弹窗),这个方法就是 "离职前的最后 10 分钟"------ 必须把自己的东西清干净:定时提醒要关(不然离职后还在公司弹窗)、未完成的请求要取消(不然给公司造垃圾数据)、绑定的事件要解绑(不然可能导致内存泄漏)。要是忘了清定时器,就像离职后还占着公司的工位,看似小事,多了会拖垮整个项目(性能下降)。

老派生命周期的 "槽点":

这么写看似逻辑清晰,但实际开发中,一个组件的 "数据请求 + 定时器 + 事件绑定" 可能分散在三个生命周期里 ------ 比如 "发请求" 在componentDidMount,"请求结果更新后同步数据" 在componentDidUpdate,"取消请求" 在componentWillUnmount。原本相关的逻辑被拆得七零八落,就像你把 "对接一个需求" 的动作,拆到 "入职、改需求、离职" 三个阶段,后期维护时要翻遍整个文件找逻辑,主打一个 "找得到开头,找不到结尾"。

这个真的挺难搞懂的,我刚接触的时候差点劝退。

二、React 17+:hooks 来了!函数组件直接 "躺赢"

hooks 一上线,直接把函数组件从 "边缘外包岗" 抬成了 "核心业务岗"------ 不用class,不用this,写代码像 "唠嗑" 一样轻松。

1. useState:状态?"随手揣兜里" 就行

想存个能触发渲染的变量?useState一句话搞定,不用constructor,不用this,主打一个 "轻装上阵"。

直接拿我第一个举的例子:

jsx 复制代码
import { useState } from 'react'
export default function App() {
    const count = 0;
    function add() {
        count += 1; 
    }
    return <button onClick={add}>{count}</button>
}

如果你这样写的话根本没用,违背了 React 函数组件的状态管理规则 。无论你按多少次按钮结果都是0

为啥凭啥?原因如下:

  • 当点击按钮执行 add 函数时,count += 1 只是在当前函数执行栈里修改了变量值,但这个修改不会通知 React "组件需要重新渲染";
  • 函数组件每次渲染都是一次独立的函数执行,即便本次执行里 count 变了,React 没感知到,就不会重新调用 App 函数,页面上显示的依然是初始渲染时的 0

这时候就得请出useState方法了:

jsx 复制代码
import { useState } from 'react'
export default function App() {
    const [count, setCount] = useState(0); // 一句话:"count是状态,setCount是修改它的按钮"
    const [list, setList] = useState([]); // 还能一次性搞多个状态,不用裹在 this.state里!
    function add() {
        setCount(count + 1); // 改状态="按按钮",直接触发渲染,没this的事儿!
    }
    return <button onClick={add}>{count}</button>
}

点击前:

点击3次后:

这样我们就成功修改了count的值。

2. useEffect:生命周期?"一个函数承包所有活"

类组件的三个生命周期,hooks 用一个useEffect就给 "合并裁员" 了,还能 "按需上班" ,主打一个 "精准摸鱼"

jsx 复制代码
import { useEffect, useState } from "react";
export default function App2() {
    const [list, setList] = useState([]);

    // 场景1:只在"入职时"发请求 → 第二个参数传空数组[]
    useEffect(() => {
        fetch('https://mock.mengxuegu.com/mock/66585c4db462b81cb3916d3e/songer/songer') // 刚入职先拉数据
        .then(res => res.json())
        .then(data => setList([...list, ...data.data]))
    }, []) // 空数组="只上一天班,之后躺平"

    // 场景2:count变了才触发 → 第二个参数传[count]
    useEffect(() => {
        console.log('count变了,我才干活');
    }, [count]) // count是"考勤机",它变了才打卡

    // 场景3:离职前清东西 → return一个函数
    useEffect(() => {
        const timer = setInterval(() => {}, 1000);
        return () => clearInterval(timer); // 离职前把定时器"关了再走"
    }, [])
}

"时间管理大师" --useEffect

  • 第二个参数传[]:"我只在组件第一次渲染后干一次活,多一次都不干";
  • [x]:"只有 x 变了,我才动一动";
  • 返回函数:"走之前把烂摊子收拾干净"。直接把类组件的三个生命周期按在地上摩擦,效率拉满!

三、总结:从 "老派" 到 "新派",到底爽在哪?

对比项 类组件(16.8 前) 函数组件 + hooks(17+)
代码量 要写 class、constructor、bind 直接 function,一行 useState 搞定
状态管理 裹在 this.state 里,this 易迷路 变量 + 修改函数分离,清爽不绕弯
生命周期 多个函数分散写,易冗余 一个 useEffect 按需配置,逻辑聚合
复用性 得写 HOC/Render Props(麻烦) 自定义 hooks 直接 "复制粘贴逻辑"

咱就是说,现在写 React 不用 hooks ,就像 "快 2026 了还在用按键手机"------ 不是不能用,但就是 "别人都在刷短视频,你在那按数字键发短信",主打一个 "慢半拍的倔强"

结语

说到底,React类组件hooks 的进化,就像把 "做饭得先砌灶台" 改成了 "点外卖还能选定制配料" ------ 少了繁琐的仪式感,多了精准的掌控力。如今的 hooks 早已是 React 的 "当家花旦",但咱也不用嫌弃类组件 "老古董",毕竟它是 hooks 的 "前辈恩师"。总之,不管是老派还是新派,能高效写好组件的,都是咱前端圈的 "好派"

这篇文章里面的知识真的难,码了很久有了这篇文章,但还不是很透彻。如果有分析的不对的地方,麻烦大佬指出😭

相关推荐
JS_GGbond2 小时前
给数组装上超能力:JavaScript数组方法趣味指南
前端·javascript
前端无涯2 小时前
Tailwind CSS v4 开发 APP 内嵌 H5:安卓 WebView 样式丢失问题解决与降级实战
前端
前端无涯2 小时前
react---JSX完全指南:从基础语法到进阶实战
react.js·前端框架
小邋遢2.02 小时前
vscod 执行npm build报错:Error: Cannot find module ‘vite‘
前端·npm·node.js
是你的小橘呀2 小时前
新手入门 React 必备:电影榜单项目核心知识点全解析
前端·javascript
yinmaisoft2 小时前
JNPF 钉钉双向同步攻略:组织 / 用户一键打通,触发事件自动联动
前端·低代码·钉钉
梨子同志2 小时前
Node.js Buffer 和 Stream
前端
鹏北海2 小时前
微信扫码登录 iframe 方案中的状态拦截陷阱
前端·javascript·vue.js
狗哥哥2 小时前
Vite 插件实战 v2:让 keep-alive 的“组件名”自动长出来
前端·vue.js·架构