当前位置: 首页 > news >正文

React学习教程,从入门到精通,React 组件核心语法知识点详解(类组件体系)(19)

📘 React 组件核心语法知识点详解(类组件体系)


一、组件的定义

React 组件有两种定义方式:

  1. 函数组件(无状态组件) —— 无内部状态,只接收 props 渲染 UI。
  2. 类组件(有状态组件) —— 继承自 React.Component,可拥有 state 和生命周期。

✅ 案例 1:函数组件定义

// 函数组件:接收 props,返回 JSX
function Welcome(props) {return <h1>Hello, {props.name}!</h1>;
}// 或使用箭头函数
const WelcomeArrow = (props) => <h1>Hello, {props.name}!</h1>;

✅ 案例 2:类组件定义

import React, { Component } from 'react';class Welcome extends Component {render() {return <h1>Hello, {this.props.name}!</h1>;}
}export default Welcome;

📌 注意:类组件必须实现 render() 方法,且必须返回合法的 JSX 或 null


二、props(属性)

props 是父组件传递给子组件的只读数据。子组件不应修改 props

✅ 案例 3:props 基本使用

// ParentComponent.jsx
import React from 'react';
import ChildComponent from './ChildComponent';class ParentComponent extends React.Component {render() {return (<div>{/* 传递字符串、数字、对象、函数等 */}<ChildComponent name="Alice" age={25} hobbies={['reading', 'coding']} greet={() => alert('Hello from Parent!')}/></div>);}
}// ChildComponent.jsx
class ChildComponent extends React.Component {render() {const { name, age, hobbies, greet } = this.props;return (<div><p>Name: {name}</p><p>Age: {age}</p><ul>{hobbies.map((hobby, index) => (<li key={index}>{hobby}</li>))}</ul><button onClick={greet}>Greet</button></div>);}
}

⚠️ props 是只读的!不要在子组件中修改:this.props.name = 'Bob'


三、state(状态)

state 是组件内部管理的可变数据,改变 state 会触发组件重新渲染。

✅ 案例 4:state 初始化与更新

class Counter extends React.Component {// 初始化 state(推荐在 constructor 中)constructor(props) {super(props);this.state = {count: 0,message: "点击按钮增加计数"};}// 更新 state 使用 this.setState()increment = () => {this.setState({count: this.state.count + 1,message: `当前计数:${this.state.count + 1}`});};render() {return (<div><p>{this.state.message}</p><button onClick={this.increment}>+1</button></div>);}
}

📌 setState() 是异步的!不要依赖当前 state 立即更新。

✅ 案例 5:使用函数式 setState(推荐用于依赖前一个状态)

increment = () => {this.setState((prevState) => ({count: prevState.count + 1,message: `当前计数:${prevState.count + 1}`}));
};

四、render()

每个类组件必须实现 render() 方法,返回要渲染的 React 元素。

✅ 案例 6:render 返回多种内容

class MyComponent extends React.Component {render() {const isLoggedIn = this.props.isLoggedIn;// 可返回:JSX / 数组 / 字符串 / 数字 / null / falseif (!isLoggedIn) {return "请先登录";}return (<div><h2>欢迎回来!</h2>{this.renderUserList()}</div>);}renderUserList() {return (<ul><li>张三</li><li>李四</li></ul>);}
}

📌 render() 必须是纯函数 —— 不能修改 state 或与浏览器交互。


五、有状态组件 vs 无状态组件

类型是否有 state是否有生命周期使用场景
有状态组件✅ 有✅ 有需要管理内部状态、交互逻辑
无状态组件❌ 无❌ 无纯展示型组件,接收 props 渲染

✅ 案例 7:对比两种组件

// 无状态组件(展示型)
function UserProfile(props) {return (<div><img src={props.avatar} alt={props.name} /><h3>{props.name}</h3><p>{props.bio}</p></div>);
}// 有状态组件(容器型)
class UserList extends React.Component {constructor(props) {super(props);this.state = {users: [{ id: 1, name: 'Alice', avatar: 'alice.jpg' },{ id: 2, name: 'Bob', avatar: 'bob.jpg' }],selectedUser: null};}selectUser = (user) => {this.setState({ selectedUser: user });};render() {return (<div><h2>用户列表</h2><ul>{this.state.users.map(user => (<li key={user.id} onClick={() => this.selectUser(user)}>{user.name}</li>))}</ul>{this.state.selectedUser && (<UserProfile name={this.state.selectedUser.name}avatar={this.state.selectedUser.avatar}/>)}</div>);}
}

六、哪些组件应该有 state?

有状态组件 应该是:

  • 负责数据获取、管理用户交互、表单输入、动画状态等。
  • 通常是“容器组件”(Container Components)。

无状态组件 应该是:

  • 只负责 UI 展示,不处理业务逻辑。
  • 通常是“展示组件”(Presentational Components)。

🎯 原则:状态尽量提升到共同父组件(状态提升),避免重复或难以同步。


七、哪些数据应该放入 state 中?

✅ 应该放入 state 的数据:

  • 会随时间变化的数据(如计数器、表单值、开关状态)
  • 影响 UI 渲染的数据
  • 无法从 props 或其他 state 计算得出的数据

✅ 案例 8:合理使用 state

class LoginForm extends React.Component {constructor(props) {super(props);this.state = {username: '',      // 用户输入,会变 → ✅ 放入 statepassword: '',      // 用户输入,会变 → ✅isLoading: false,  // 请求状态,影响 UI → ✅errors: {}         // 表单校验错误 → ✅};}handleChange = (e) => {this.setState({[e.target.name]: e.target.value});};render() {return (<form><input name="username" value={this.state.username} onChange={this.handleChange} /><input name="password" type="password" value={this.state.password} onChange={this.handleChange} />{this.state.isLoading && <p>登录中...</p>}</form>);}
}

八、哪些数据不应该放入 state 中?

❌ 不应放入 state 的数据:

  • 从 props 直接计算得出的数据(应在 render 中计算)
  • 不影响 UI 的数据(如定时器 ID、非响应式数据)
  • 可通过其他 state 或 props 推导出的数据(避免冗余)

✅ 案例 9:避免冗余 state

class UserDisplay extends React.Component {constructor(props) {super(props);this.state = {firstName: '张',lastName: '三'// ❌ 不要:fullName: '张三' —— 可通过计算得到};}// ✅ 在 render 或 getter 中计算get fullName() {return `${this.state.firstName} ${this.state.lastName}`;}render() {return <h2>欢迎 {this.fullName}</h2>;}
}

📌 也可在 render() 中直接计算:{this.state.firstName + ' ' + this.state.lastName}


九、ref 引用(访问 DOM 或类组件实例)

用于直接访问 DOM 元素或类组件实例(函数组件不能直接 ref,需 forwardRef)。

三种方式:

  1. createRef()(推荐)
  2. 回调函数方式
  3. 字符串方式(已废弃,不推荐)

✅ 案例 10:createRef() 方式(推荐)

class TextInput extends React.Component {constructor(props) {super(props);// 创建 refthis.inputRef = React.createRef();}focusInput = () => {// 访问原生 DOM 元素this.inputRef.current.focus();};render() {return (<div><input ref={this.inputRef} type="text" placeholder="点击按钮聚焦" /><button onClick={this.focusInput}>聚焦输入框</button></div>);}
}

✅ 案例 11:回调函数方式

class TextInputCallback extends React.Component {constructor(props) {super(props);this.inputElement = null; // 保存 DOM 引用}setInputRef = (element) => {this.inputElement = element;};focusInput = () => {if (this.inputElement) {this.inputElement.focus();}};render() {return (<div><input ref={this.setInputRef} type="text" /><button onClick={this.focusInput}>聚焦(回调 ref)</button></div>);}
}

❌ 案例 12:字符串方式(已废弃,仅作了解)

// 不推荐!未来会被移除
class OldTextInput extends React.Component {focusInput = () => {this.refs.myInput.focus(); // ❌ 不推荐};render() {return (<input ref="myInput" /> {/* ❌ 字符串 ref */});}
}

十、props 属性验证(PropTypes)

用于在开发环境中对 props 进行类型检查。

✅ 案例 13:PropTypes 基本使用

import PropTypes from 'prop-types';class UserProfile extends React.Component {render() {return (<div><h3>{this.props.name}</h3><p>年龄:{this.props.age}</p><p>是否管理员:{this.props.isAdmin ? '是' : '否'}</p></div>);}
}// 定义 props 类型和是否必填
UserProfile.propTypes = {name: PropTypes.string.isRequired,age: PropTypes.number,isAdmin: PropTypes.bool
};// 设置默认 props
UserProfile.defaultProps = {age: 18,isAdmin: false
};export default UserProfile;

💡 常用 PropTypes:

PropTypes.string
PropTypes.number
PropTypes.bool
PropTypes.func
PropTypes.object
PropTypes.array
PropTypes.node          // 任何可渲染的东西
PropTypes.element       // React 元素
PropTypes.instanceOf(MyClass)
PropTypes.oneOf(['News', 'Photos'])
PropTypes.shape({color: PropTypes.string,fontSize: PropTypes.number
})

十一、组件的其他成员

1. 静态成员(static)

class MyComponent extends React.Component {static myStaticMethod() {return '我是静态方法';}static defaultProps = { theme: 'light' };render() {return <div>主题:{this.props.theme}</div>;}
}// 外部调用
console.log(MyComponent.myStaticMethod()); // ✅

2. 自定义方法(绑定 this)

class ClickCounter extends React.Component {constructor(props) {super(props);this.state = { count: 0 };// 方法1:在 constructor 中 bindthis.handleClick = this.handleClick.bind(this);}// 方法2:使用箭头函数(推荐)handleClick = () => {this.setState({ count: this.state.count + 1 });};render() {return <button onClick={this.handleClick}>点击了 {this.state.count} 次</button>;}
}

3. 生命周期方法(简要提及)

class LifecycleDemo extends React.Component {componentDidMount() {console.log('组件挂载完成');}componentDidUpdate(prevProps, prevState) {if (prevState.count !== this.state.count) {console.log('计数更新了');}}componentWillUnmount() {console.log('组件即将卸载');}render() { ... }
}

🧩 综合性实战案例

✅ 案例 14:TodoList 应用(完整类组件实现)

import React, { Component } from 'react';
import PropTypes from 'prop-types';// 子组件:单个 Todo 项
class TodoItem extends Component {render() {const { todo, onDelete, onToggle } = this.props;return (<li style={{ textDecoration: todo.completed ? 'line-through' : 'none',color: todo.completed ? '#999' : '#000'}}><input type="checkbox" checked={todo.completed} onChange={() => onToggle(todo.id)} /><span>{todo.text}</span><button onClick={() => onDelete(todo.id)}>删除</button></li>);}
}TodoItem.propTypes = {todo: PropTypes.shape({id: PropTypes.number.isRequired,text: PropTypes.string.isRequired,completed: PropTypes.bool.isRequired}).isRequired,onDelete: PropTypes.func.isRequired,onToggle: PropTypes.func.isRequired
};// 主组件:TodoList
class TodoList extends Component {constructor(props) {super(props);this.state = {todos: [{ id: 1, text: '学习 React', completed: false },{ id: 2, text: '写组件文档', completed: true }],inputText: '',filter: 'all' // all / active / completed};this.inputRef = React.createRef();}addTodo = () => {const text = this.state.inputText.trim();if (text === '') return;const newTodo = {id: Date.now(),text: text,completed: false};this.setState(prevState => ({todos: [...prevState.todos, newTodo],inputText: ''}), () => {// 回调:添加后聚焦输入框this.inputRef.current.focus();});};deleteTodo = (id) => {this.setState(prevState => ({todos: prevState.todos.filter(todo => todo.id !== id)}));};toggleTodo = (id) => {this.setState(prevState => ({todos: prevState.todos.map(todo =>todo.id === id ? { ...todo, completed: !todo.completed } : todo)}));};handleInputChange = (e) => {this.setState({ inputText: e.target.value });};setFilter = (filter) => {this.setState({ filter });};getFilteredTodos = () => {const { todos, filter } = this.state;switch (filter) {case 'active':return todos.filter(t => !t.completed);case 'completed':return todos.filter(t => t.completed);default:return todos;}};render() {const { inputText, filter } = this.state;const filteredTodos = this.getFilteredTodos();const activeCount = this.state.todos.filter(t => !t.completed).length;return (<div style={{ padding: '20px', fontFamily: 'Arial' }}><h1>Todo List</h1>{/* 输入区域 */}<div><inputref={this.inputRef}type="text"value={inputText}onChange={this.handleInputChange}onKeyPress={(e) => e.key === 'Enter' && this.addTodo()}placeholder="添加新任务..."style={{ padding: '8px', width: '300px' }}/><button onClick={this.addTodo} style={{ padding: '8px 16px' }}>添加</button></div>{/* 过滤器 */}<div style={{ margin: '10px 0' }}><button onClick={() => this.setFilter('all')}style={{ marginRight: '8px' }}>全部 ({this.state.todos.length})</button><button onClick={() => this.setFilter('active')}style={{ marginRight: '8px' }}>未完成 ({activeCount})</button><button onClick={() => this.setFilter('completed')}>已完成 ({this.state.todos.length - activeCount})</button></div>{/* 列表 */}<ul style={{ listStyle: 'none', padding: 0 }}>{filteredTodos.map(todo => (<TodoItemkey={todo.id}todo={todo}onDelete={this.deleteTodo}onToggle={this.toggleTodo}/>))}</ul>{filteredTodos.length === 0 && <p>暂无任务</p>}</div>);}
}export default TodoList;

✅ 案例 15:表单验证组件(含 ref、state、props)

class ValidatedForm extends React.Component {constructor(props) {super(props);this.state = {email: '',password: '',errors: {email: '',password: ''}};this.emailRef = React.createRef();this.passwordRef = React.createRef();}validateEmail = (email) => {const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;return re.test(email);};validatePassword = (pwd) => {return pwd.length >= 6;};handleChange = (field) => (e) => {const value = e.target.value;this.setState(prevState => ({[field]: value,errors: {...prevState.errors,[field]: ''}}));};handleSubmit = (e) => {e.preventDefault();let valid = true;const newErrors = { email: '', password: '' };if (!this.validateEmail(this.state.email)) {newErrors.email = '请输入有效的邮箱';valid = false;}if (!this.validatePassword(this.state.password)) {newErrors.password = '密码至少6位';valid = false;}this.setState({ errors: newErrors });if (valid) {alert('表单提交成功!');// 提交逻辑} else {// 聚焦第一个错误字段if (newErrors.email) {this.emailRef.current.focus();} else if (newErrors.password) {this.passwordRef.current.focus();}}};render() {const { email, password, errors } = this.state;return (<form onSubmit={this.handleSubmit} style={{ maxWidth: '400px', margin: '0 auto' }}><div style={{ marginBottom: '15px' }}><label>Email:</label><inputref={this.emailRef}type="email"value={email}onChange={this.handleChange('email')}style={{ display: 'block', width: '100%', padding: '8px' }}/>{errors.email && <span style={{ color: 'red' }}>{errors.email}</span>}</div><div style={{ marginBottom: '15px' }}><label>密码:</label><inputref={this.passwordRef}type="password"value={password}onChange={this.handleChange('password')}style={{ display: 'block', width: '100%', padding: '8px' }}/>{errors.password && <span style={{ color: 'red' }}>{errors.password}</span>}</div><button type="submit" style={{ padding: '10px 20px' }}>登录</button></form>);}
}

✅ 总结要点

知识点关键要点
组件定义函数组件轻量,类组件有状态和生命周期
props只读,父传子,类型检查用 PropTypes
state可变状态,用 setState 更新,避免直接修改
render()必须实现,返回 JSX/null/字符串等,必须是纯函数
有状态 vs 无状态容器组件管状态,展示组件只渲染
state 数据选择会变、影响 UI、不可推导的数据放入 state
refcreateRef() 最推荐,用于访问 DOM 或组件实例
综合案例TodoList、表单验证 —— 涵盖 state、props、事件、ref、验证等完整流程

✅ 以上内容全面覆盖 React 类组件核心语法体系,适合系统学习和面试复习。建议结合 Hooks 体系(如 useState, useEffect)进一步学习现代 React 开发。

如需 Hooks 版本或函数组件进阶,欢迎继续提问!


📌 最后提醒:虽然类组件仍广泛使用,但 React 官方推荐新项目使用 函数组件 + Hooks,更简洁、易测试、逻辑复用更方便。但理解类组件对阅读老项目和面试至关重要!


文章转载自:

http://m5VqXBNV.rnsjp.cn
http://BzZYOtBx.rnsjp.cn
http://wiwIB0Ua.rnsjp.cn
http://CFdH9AH3.rnsjp.cn
http://ImA9HXPL.rnsjp.cn
http://JJR2W0Mv.rnsjp.cn
http://Wa5Nu0Uo.rnsjp.cn
http://kDNPnfoR.rnsjp.cn
http://7DH0CtqT.rnsjp.cn
http://QS3TsvE1.rnsjp.cn
http://dCDLfw30.rnsjp.cn
http://rs802f0L.rnsjp.cn
http://G9lLIWTd.rnsjp.cn
http://kjyjHoK7.rnsjp.cn
http://3s4d81FV.rnsjp.cn
http://03xWVblb.rnsjp.cn
http://Ju4mI5Mt.rnsjp.cn
http://9QYyrh0b.rnsjp.cn
http://sjqyGOw5.rnsjp.cn
http://PxR3p7Lm.rnsjp.cn
http://Sy3mokEv.rnsjp.cn
http://OLFFgSqv.rnsjp.cn
http://OOjdEmzo.rnsjp.cn
http://UYbEwvIA.rnsjp.cn
http://wTYZYpCl.rnsjp.cn
http://F6SUpmCd.rnsjp.cn
http://dAZgKiio.rnsjp.cn
http://MYduIssK.rnsjp.cn
http://eCh2EZph.rnsjp.cn
http://cru9vMKu.rnsjp.cn
http://www.dtcms.com/a/382844.html

相关文章:

  • Java分布式编程:RMI机制
  • 5-12 WPS JS宏 Range数组规范性测试
  • MySQL 的安装、启动、连接(Windows、macOS 和 Linux)
  • (附源码)基于Spring Boot的宿舍管理系统设计
  • Mac下Python3安装
  • C++数组与字符串:从基础到实战技巧
  • 第13课:分布式Agent系统
  • Docker 容器化部署核心实战——Nginx 服务配置与正反向代理原理解析
  • 【分享】中小学教材课本 PDF 资源获取指南
  • 如何用 Git Hook 和 CI 流水线为 FastAPI 项目保驾护航?
  • 安卓旋转屏幕后如何防止数据丢失-ViewModel入门
  • STM32_05_时钟树
  • 元宇宙与体育产业:沉浸式体验重构体育全链条生态
  • LeetCode 每日一题 966. 元音拼写检查器
  • C++密码锁 2023年CSP-S认证真题 CCF信息学奥赛C++ 中小学提高组 第二轮真题解析
  • Vue3 视频播放器完整指南 – @videojs-player/vue 从入门到精通
  • 零售企业数字化转型的道、法、术:基于开源AI大模型AI智能名片S2B2C商城小程序的战略重构
  • 【编号500】(道路分类)广东路网数据广东路网分类数据(2025年)
  • 【PHP7内核剖析】-1.3 FPM
  • 网络编程之UDP广播与粘包问题
  • h3笔记:polygon
  • Unity 性能优化 之 编辑器创建资源优化( 工作流 | 场景 | 预制体)
  • 《Python Web部署应知应会》No3:Flask网站的性能优化和实时监测深度实战
  • 《嵌入式硬件(十):基于IMX6ULL的按键操作》
  • JVM默认栈大小
  • 深度学习实战指南:从神经网络基础到模型优化的完整攻略
  • 浏览器性能测试深度解析:指标、工具与优化实践
  • 【嵌入式DIY实例-ESP32篇】-3D姿态测量(Pitch, Roll, Yaw)
  • LeetCode 0966.元音拼写检查器:三个哈希表实现
  • 深入浅出 HarmonyOS 应用开发:ArkTS 声明式 UI 与状态管理最佳实践