
React 类组件完全指南用 class 语法管理 props、state 与 this【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum本篇技术指南系统讲解 React 中的类组件class-based component写法从extends Component的最小骨架到 constructor 中初始化 state、通过this访问 props、用setState更新状态再到处理this绑定这一核心难点。文章以本仓库 class_based_components.md 课程为骨架结合仓库内 React 入门与生命周期相关章节帮助你读懂遗留代码库、实现函数组件到类组件的等价改写并理解类组件生命周期方法与useEffect钩子之间的对应关系。为什么今天还要学习类组件你此前接触到的 React 组件大多是函数组件functional component——以普通 JavaScript 函数的形式定义并返回 JSX。这种写法是目前的主流但翻开任何稍早上线的 React 代码库你都会看到大量class语法的身影它们被称为类组件。这背后有一段重要的历史在 2019 年 2 月 React Hooks 正式引入之前函数组件也被称为无状态组件state-less因为当时函数组件内部没有任何管理 state 的机制状态只能由类组件持有。Hooks 的诞生改变了这一切让函数组件变得简洁、少样板代码成为社区推荐的首选写法。但掌握类组件依然是你职业生涯的必备技能你会遇到需要维护的遗留代码会在别人的 Pull Request 中看到类组件也会需要把旧代码渐进迁移到新写法。本课的核心目标正是让你理解类组件是如何被写出来的以及 props、state 这些你在函数组件中已经熟悉的概念在类组件中是如何被使用的。类组件的最小骨架extends Component类组件本质上仍是一个 JavaScript 类但它必须拥有 React 组件应具备的特性。React 把所有这些特性都封装在名为Component的基类上我们只需让自定义类继承它import { Component } from react; class ClassInput extends Component { // Some code goes here } export default ClassInput;注意这里的import写法Component需要用花括号包裹具名导入因为它不是react模块的默认导出。正如仓库中 react_introduction.md 所解释的默认导出无需花括号其余导出都必须用花括号包裹。从源码结构看extends Component这一步相当于React 化Reactifying了我们的类——它一次性获得了 React 组件应有的所有方法与属性。同时请记住 React 组件无论类还是函数的命名约定必须使用 PascalCase 首字母大写。JSX 解析时正是依靠首字母大小写来区分原生 HTML 标签如greeting /会被当作普通 HTML 元素与 React 组件实例Greeting /。constructor 与 propssuper 到底做了什么光有骨架还不够接下来要为类加上构造函数constructor。组件接收到的 props 会被传入构造函数并连同super一起使用从而让 props 可以在this的上下文中被访问——这里的this指的就是组件实例本身import { Component } from react; class ClassInput extends Component { constructor(props) { super(props); } // Some more code goes here } export default ClassInput;super(props)是 React 类组件构造函数中必须调用的一步它负责调用父类Component的构造函数并让this.props在组件内部可用。如果你的组件不接收任何 props构造函数与super可以不带参数甚至整个构造函数都可以省略——正如 react_introduction.md 所指出的构造函数在类组件中不是强制项但它涉及继承与状态时变得重要因此你几乎总会在真实代码中见到它。关于 defaultProps 的补充在函数组件中你可以通过参数默认值给 props 兜底而在旧代码库的类组件中你更可能见到defaultProps这种传统写法ClassInput.defaultProps { name: My Todos, };仓库中的 passing_data_between_components.md 特别提醒React 已不再接受函数组件使用defaultProps但理解它仍然有用——尤其是在阅读类组件或维护旧代码库时。render 方法类组件如何渲染 JSXprops 能在类内部访问之后下一个问题就是如何输出 JSX。答案是把 JSX 从render方法中返回。注意与函数组件不同类组件中 props 是通过this.props提供的而不是作为函数参数import { Component } from react; class ClassInput extends Component { constructor(props) { super(props); } // Some more code goes here render() { return ( section h3{this.props.name}/h3 {/* The input field to enter Todos */} form label htmlFortask-entryEnter a task: /label input typetext idtask-entry nametask-entry / button typesubmitSubmit/button /form h4All the tasks!/h4 {/* The list of all the Todos, displayed */} ul/ul /section ); } } export default ClassInput;render是类组件中使用频率最高的方法也是类组件唯一必填的方法详见后文生命周期小节。它负责在组件挂载mount和更新update时执行并且应当保持纯净不修改组件状态、在输入相同的情况下返回相同结果、不直接与浏览器交互。state 的初始化与更新setState 的不可变性约定接下来是状态管理。在类组件中state 作为构造函数的一部分被初始化import { Component } from react; class ClassInput extends Component { constructor(props) { super(props); this.state { todos: [], inputVal: , }; } // Some more code goes here render() { return ( section h3{this.props.name}/h3 form label htmlFortask-entryEnter a task: /label input typetext idtask-entry nametask-entry / button typesubmitSubmit/button /form h4All the tasks!/h4 ul/ul /section ); } } export default ClassInput;初始化之后更新 state 只能通过 React 预定义的setState方法完成。这里有一条铁律state 不可被直接修改mutate每次更新都必须设置一个新的状态对象。下面这个完整示例体现了正确的更新方式——处理输入框变化时使用函数式更新并展开旧 state提交表单时用concat而不是push生成新数组push会原地修改数组违背不可变性import { Component } from react; class ClassInput extends Component { constructor(props) { super(props); this.state { todos: [], inputVal: , }; this.handleInputChange this.handleInputChange.bind(this); this.handleSubmit this.handleSubmit.bind(this); } handleInputChange(e) { this.setState((state) ({ ...state, inputVal: e.target.value, })); } handleSubmit(e) { e.preventDefault(); this.setState((state) ({ todos: state.todos.concat(state.inputVal), inputVal: , })); } render() { return ( section h3{this.props.name}/h3 form onSubmit{this.handleSubmit} label htmlFortask-entryEnter a task: /label input typetext idtask-entry nametask-entry value{this.state.inputVal} onChange{this.handleInputChange} / button typesubmitSubmit/button /form h4All the tasks!/h4 ul {this.state.todos.map((todo) ( li key{todo}{todo}/li ))} /ul /section ); } } export default ClassInput;这段代码与仓库中配套示例的src/components/FunctionalInput.jsx函数组件版实现的是完全相同的功能受控输入、表单提交、任务列表渲染。你可以通过npm install安装依赖、npm run dev启动开发服务器在同一页面中对比这两个版本。this 绑定类方法最大的坑仔细观察上面示例的构造函数你会发现多出了两行this.handleInputChange this.handleInputChange.bind(this); this.handleSubmit this.handleSubmit.bind(this);这正是类组件与函数组件差异最大、也最容易出错的地方默认情况下类中声明的方法并不绑定到类实例上因此直接把这些方法作为事件处理器传入如onSubmit{this.handleSubmit}时方法内部的this会丢失组件实例的上下文。标准做法是在构造函数内部完成绑定而不是在运行时render 方法中绑定这样每个实例只绑定一次避免重复创建函数带来的性能开销constructor(props) { super(props); // ... this.handleInputChange this.handleInputChange.bind(this); this.handleSubmit this.handleSubmit.bind(this); }如果你希望完全绕开bind可以改用箭头函数语法定义类方法。箭头函数不拥有自己的this它会自动捕获定义时所在作用域的this——也就是类实例因此无需在构造函数中额外绑定handleInputChange (e) { this.setState((state) ({ ...state, inputVal: e.target.value })); }; handleSubmit (e) { e.preventDefault(); this.setState((state) ({ todos: state.todos.concat(state.inputVal), inputVal: , })); };两种写法在功能上等价选择哪一种更多取决于团队代码风格与维护的旧代码约定。类组件的生命周期方法类组件的另一个核心主题是生命周期lifecycle。一个组件的一生分为三个阶段挂载mounting、更新updating、卸载unmounting每个阶段在类组件中都有对应的生命周期方法。仓库中同目录的 component_lifecycle_methods.md 对此有系统讲解下面逐一说明。render()前面已经用过render是类组件唯一必填的生命周期方法在组件挂载与更新时都会运行且必须保持纯净不修改状态、不直接操作浏览器。componentDidMount()组件被插入 DOM 树之后立即运行。需要的数据请求fetch应该放在这里任何依赖组件已挂载到 DOM 的操作也适合在此执行。componentDidUpdate()组件重新渲染之后运行。在此方法中更新状态要格外小心如果无条件地更新 state会触发再次渲染从而陷入无限循环。避免方法是基于前后 props 是否相等之类的条件判断再更新。它的典型用途是响应 DOM 变化或某些状态变化做后续处理例如当用户变化时重新拉取用户数据。componentWillUnmount()组件被卸载、销毁之前调用的最后一个生命周期方法。这里应该做清理工作取消网络请求、清除定时器等防止内存泄漏与无效更新。useEffect 与生命周期方法的对应关系函数组件中没有这些生命周期方法取而代之的是useEffect钩子。理解二者对应关系有助于你在阅读新旧两种代码时自由切换。useEffect本质上是componentDidMount、componentDidUpdate与componentWillUnmount的组合具体对应哪个取决于依赖数组与返回值useEffect 写法等价的生命周期方法空依赖数组[]componentDidMount带值的依赖数组[a, b]componentDidMountcomponentDidUpdate仅在依赖变化时不传依赖数组componentDidMountcomponentDidUpdate回调内返回函数componentWillUnmount通过返回的清理函数实现例如下面的写法同时包含componentDidMount与componentWillUnmount的功能由于依赖数组为空不包含componentDidUpdateuseEffect(() { placeholderFunction(); return () cleanupFunction(); }, [])仓库中的 how_to_deal_with_side_effects.md 以时钟组件为例展示了完整的启动定时器 → 依赖数组 → 清理函数链条其中用clearInterval清理定时器的模式正是类组件中componentWillUnmount职责的钩子化表达。动手实践为 ClassInput 扩展功能既然已经有了一个功能完整的类组件接下来请你为它添加更多能力这正是本课配套的练习删除任务为每个任务实现一个删除按钮点击后从 state 数组中移除对应任务。样式不是重点但按钮标签应保留浏览器默认样式。任务计数实现一个新的类组件Count实时显示当前 todos 的数量并把Count渲染到ClassInput组件内的合适位置。编辑任务为每个任务实现编辑按钮。点击后把该 todo 替换为输入框并把按钮本身改为 Resubmit以便保存修改。这是三个任务中难度最高的一个。练习时请牢记本节的两个关键点更新 state 永远通过setState并保持不可变新增的方法如果使用普通函数语法记得在构造函数中绑定this或直接改用箭头函数。知识检查以下是本课的核心要点回顾检验你是否真正掌握类组件中如何获取 props通过构造函数参数接收并经super(props)后以this.props访问。类组件如何显示 JSX从render方法中返回 JSX。类组件如何处理 state在构造函数中通过this.state初始化更新时必须调用setState并传入新状态严禁直接修改旧 state。如何恢复方法中this的上下文在构造函数中使用bind绑定或直接用箭头函数语法定义方法让this自动指向类实例。类组件唯一必填的生命周期方法是什么render。初次数据请求应放在componentDidMount响应 DOM 或状态变化应放在componentDidUpdate清理操作应放在componentWillUnmountuseEffect钩子则是这些方法的函数式组合。【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考