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

React组件完全指南

1. 组件基础概念

什么是React组件?

React组件是构建用户界面的基本单元,它是一个可复用的代码片段,接收输入(props)并返回描述界面应该如何显示的React元素。

组件的核心功能

  • 封装性:将相关的逻辑和UI封装在一起
  • 可复用性:可在不同地方重复使用
  • 组合性:可以组合多个组件构建复杂界面
  • 单向数据流:数据从父组件向子组件流动

2. 组件类型

2.1 函数组件(推荐)

// 基础函数组件
function Welcome(props) {return <h1>Hello, {props.name}!</h1>;
}// 箭头函数组件
const Welcome = (props) => {return <h1>Hello, {props.name}!</h1>;
}// 带Hooks的函数组件
import { useState, useEffect } from 'react';function Counter() {const [count, setCount] = useState(0);useEffect(() => {document.title = `Count: ${count}`;}, [count]);return (<div><p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>Click me</button></div>);
}

2.2 类组件(传统方式)

import React, { Component } from 'react';class Welcome extends Component {constructor(props) {super(props);this.state = {count: 0};}componentDidMount() {document.title = `Count: ${this.state.count}`;}componentDidUpdate() {document.title = `Count: ${this.state.count}`;}handleClick = () => {this.setState({ count: this.state.count + 1 });}render() {return (<div><p>You clicked {this.state.count} times</p><button onClick={this.handleClick}>Click me</button></div>);}
}

3. Props(属性)

3.1 Props基础用法

// 父组件传递props
function App() {return (<div><UserCard name="张三" age={25} email="zhangsan@example.com"isActive={true}/></div>);
}// 子组件接收props
function UserCard({ name, age, email, isActive }) {return (<div className={`user-card ${isActive ? 'active' : ''}`}><h3>{name}</h3><p>年龄: {age}</p><p>邮箱: {email}</p></div>);
}

3.2 Props默认值和类型检查

import PropTypes from 'prop-types';function Button({ children, variant, size, onClick }) {return (<button className={`btn btn-${variant} btn-${size}`}onClick={onClick}>{children}</button>);
}// 默认props
Button.defaultProps = {variant: 'primary',size: 'medium'
};// PropTypes类型检查
Button.propTypes = {children: PropTypes.node.isRequired,variant: PropTypes.oneOf(['primary', 'secondary', 'danger']),size: PropTypes.oneOf(['small', 'medium', 'large']),onClick: PropTypes.func
};

3.3 Props传递技巧

// 1. 展开运算符传递props
const userProps = {name: "李四",age: 30,email: "lisi@example.com"
};<UserCard {...userProps} />// 2. 透传props
function CardWrapper({ children, ...otherProps }) {return (<div className="card-wrapper"><Card {...otherProps}>{children}</Card></div>);
}// 3. render props模式
function DataFetcher({ url, render }) {const [data, setData] = useState(null);const [loading, setLoading] = useState(true);useEffect(() => {fetch(url).then(res => res.json()).then(data => {setData(data);setLoading(false);});}, [url]);return render({ data, loading });
}// 使用render props
<DataFetcher url="/api/users"render={({ data, loading }) => (loading ? <div>加载中...</div> : <UserList users={data} />)}
/>

4. State(状态)

4.1 useState Hook

import { useState } from 'react';function FormExample() {// 基础状态const [name, setName] = useState('');const [email, setEmail] = useState('');// 对象状态const [user, setUser] = useState({name: '',email: '',age: 0});// 数组状态const [items, setItems] = useState([]);// 更新对象状态const updateUser = (field, value) => {setUser(prevUser => ({...prevUser,[field]: value}));};// 更新数组状态const addItem = (item) => {setItems(prevItems => [...prevItems, item]);};const removeItem = (index) => {setItems(prevItems => prevItems.filter((_, i) => i !== index));};return (<form><input value={name}onChange={e => setName(e.target.value)}placeholder="姓名"/><input value={email}onChange={e => setEmail(e.target.value)}placeholder="邮箱"/></form>);
}

4.2 状态更新最佳实践

// ❌ 错误:直接修改状态
const [user, setUser] = useState({ name: 'John', age: 25 });
user.age = 26; // 不要这样做
setUser(user);// ✅ 正确:创建新对象
setUser(prevUser => ({ ...prevUser, age: 26 }));// ❌ 错误:依赖当前状态的更新
const [count, setCount] = useState(0);
setCount(count + 1);// ✅ 正确:使用函数式更新
setCount(prevCount => prevCount + 1);

5. 常用Hooks

5.1 useEffect - 副作用处理

import { useState, useEffect } from 'react';function UserProfile({ userId }) {const [user, setUser] = useState(null);const [loading, setLoading] = useState(true);// 组件挂载和userId变化时执行useEffect(() => {let cancelled = false;const fetchUser = async () => {setLoading(true);try {const response = await fetch(`/api/users/${userId}`);const userData = await response.json();if (!cancelled) {setUser(userData);setLoading(false);}} catch (error) {if (!cancelled) {setLoading(false);}}};fetchUser();// 清理函数,防止内存泄漏return () => {cancelled = true;};}, [userId]);// 只在挂载时执行一次useEffect(() => {document.title = '用户资料';return () => {document.title = '应用名称'; // 清理};}, []);if (loading) return <div>加载中...</div>;return (<div><h1>{user?.name}</h1><p>{user?.email}</p></div>);
}

5.2 useContext - 跨组件状态共享

import { createContext, useContext, useState } from 'react';// 创建Context
const ThemeContext = createContext();// Provider组件
function ThemeProvider({ children }) {const [theme, setTheme] = useState('light');const toggleTheme = () => {setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');};return (<ThemeContext.Provider value={{ theme, toggleTheme }}>{children}</ThemeContext.Provider>);
}// 使用Context的组件
function ThemedButton() {const { theme, toggleTheme } = useContext(ThemeContext);return (<button className={`btn btn-${theme}`}onClick={toggleTheme}>当前主题: {theme}</button>);
}

5.3 useMemo 和 useCallback - 性能优化

import { useState, useMemo, useCallback } from 'react';function ExpensiveComponent({ items, filter }) {const [count, setCount] = useState(0);// 缓存昂贵的计算const filteredItems = useMemo(() => {console.log('过滤计算执行');return items.filter(item => item.name.toLowerCase().includes(filter.toLowerCase()));}, [items, filter]);// 缓存回调函数const handleItemClick = useCallback((item) => {console.log('点击了:', item.name);}, []);// 缓存派生状态const itemCount = useMemo(() => filteredItems.length, [filteredItems]);return (<div><p>计数: {count}</p><p>过滤后项目数: {itemCount}</p><button onClick={() => setCount(c => c + 1)}>增加计数</button>{filteredItems.map(item => (<ItemComponent key={item.id}item={item}onClick={handleItemClick}/>))}</div>);
}

6. 组件设计模式

6.1 容器组件和展示组件

// 容器组件 - 处理逻辑和状态
function UserListContainer() {const [users, setUsers] = useState([]);const [loading, setLoading] = useState(true);useEffect(() => {fetchUsers().then(data => {setUsers(data);setLoading(false);});}, []);const handleDeleteUser = (userId) => {setUsers(users.filter(user => user.id !== userId));};return (<UserListPresentation users={users}loading={loading}onDeleteUser={handleDeleteUser}/>);
}// 展示组件 - 只负责渲染
function UserListPresentation({ users, loading, onDeleteUser }) {if (loading) {return <div>加载中...</div>;}return (<div>{users.map(user => (<UserCard key={user.id}user={user}onDelete={() => onDeleteUser(user.id)}/>))}</div>);
}

6.2 高阶组件 (HOC)

// 高阶组件 - 添加loading功能
function withLoading(WrappedComponent) {return function WithLoadingComponent(props) {if (props.loading) {return <div>加载中...</div>;}return <WrappedComponent {...props} />;};
}// 使用HOC
const UserListWithLoading = withLoading(UserList);// 使用
<UserListWithLoading users={users} loading={isLoading} />

6.3 自定义Hook

// 自定义Hook - 数据获取
function useApi(url) {const [data, setData] = useState(null);const [loading, setLoading] = useState(true);const [error, setError] = useState(null);useEffect(() => {let cancelled = false;const fetchData = async () => {try {setLoading(true);const response = await fetch(url);const result = await response.json();if (!cancelled) {setData(result);setError(null);}} catch (err) {if (!cancelled) {setError(err);}} finally {if (!cancelled) {setLoading(false);}}};fetchData();return () => {cancelled = true;};}, [url]);return { data, loading, error };
}// 使用自定义Hook
function UserProfile({ userId }) {const { data: user, loading, error } = useApi(`/api/users/${userId}`);if (loading) return <div>加载中...</div>;if (error) return <div>错误: {error.message}</div>;return (<div><h1>{user.name}</h1><p>{user.email}</p></div>);
}

7. 性能优化

7.1 React.memo - 防止不必要的重渲染

import { memo } from 'react';// 使用memo包装组件
const UserCard = memo(function UserCard({ user, onEdit }) {console.log('UserCard渲染');return (<div><h3>{user.name}</h3><p>{user.email}</p><button onClick={() => onEdit(user.id)}>编辑</button></div>);
});// 自定义比较函数
const UserCardWithCustomCompare = memo(UserCard, (prevProps, nextProps) => {return prevProps.user.id === nextProps.user.id &&prevProps.user.name === nextProps.user.name;
});

7.2 列表优化

// ✅ 正确:使用稳定的key
function TodoList({ todos }) {return (<ul>{todos.map(todo => (<TodoItem key={todo.id} todo={todo} />))}</ul>);
}// ❌ 错误:使用索引作为key(在动态列表中)
function TodoList({ todos }) {return (<ul>{todos.map((todo, index) => (<TodoItem key={index} todo={todo} />))}</ul>);
}

7.3 代码分割和懒加载

import { lazy, Suspense } from 'react';// 懒加载组件
const HeavyComponent = lazy(() => import('./HeavyComponent'));function App() {return (<div><Suspense fallback={<div>加载中...</div>}><HeavyComponent /></Suspense></div>);
}

8. 最佳实践

8.1 组件命名和结构

// ✅ 好的命名
function UserProfileCard({ user, isEditable, onEdit }) {return (<div className="user-profile-card"><UserAvatar src={user.avatar} alt={user.name} /><UserInfo user={user} />{isEditable && (<EditButton onClick={() => onEdit(user.id)} />)}</div>);
}// ✅ 清晰的文件结构
components/UserProfileCard/index.js          // 导出组件UserProfileCard.jsxUserProfileCard.cssUserProfileCard.test.js

8.2 事件处理

function TodoItem({ todo, onToggle, onDelete }) {// ✅ 使用useCallback优化const handleToggle = useCallback(() => {onToggle(todo.id);}, [todo.id, onToggle]);const handleDelete = useCallback(() => {onDelete(todo.id);}, [todo.id, onDelete]);// ✅ 或者在JSX中直接传递参数return (<div><input type="checkbox" checked={todo.completed}onChange={() => onToggle(todo.id)}/><span>{todo.text}</span><button onClick={() => onDelete(todo.id)}>删除</button></div>);
}

8.3 错误处理

import { ErrorBoundary } from 'react-error-boundary';// 错误边界组件
function ErrorFallback({ error, resetErrorBoundary }) {return (<div role="alert"><h2>出错了:</h2><pre>{error.message}</pre><button onClick={resetErrorBoundary}>重试</button></div>);
}// 使用错误边界
function App() {return (<ErrorBoundaryFallbackComponent={ErrorFallback}onError={(error, errorInfo) => {console.error('错误记录:', error, errorInfo);}}onReset={() => {// 重置状态}}><MyComponent /></ErrorBoundary>);
}

9. 常见注意事项

9.1 避免常见错误

// ❌ 错误:在render中定义函数
function BadComponent() {return (<button onClick={() => {// 每次渲染都创建新函数console.log('clicked');}}>点击</button>);
}// ✅ 正确:使用useCallback或提取到组件外部
function GoodComponent() {const handleClick = useCallback(() => {console.log('clicked');}, []);return <button onClick={handleClick}>点击</button>;
}// ❌ 错误:在useEffect中缺少依赖
function BadEffect({ userId }) {const [user, setUser] = useState(null);useEffect(() => {fetchUser(userId).then(setUser);}, []); // 缺少userId依赖return <div>{user?.name}</div>;
}// ✅ 正确:包含所有依赖
function GoodEffect({ userId }) {const [user, setUser] = useState(null);useEffect(() => {fetchUser(userId).then(setUser);}, [userId]); // 包含userId依赖return <div>{user?.name}</div>;
}

9.2 内存泄漏防护

function ComponentWithCleanup() {const [data, setData] = useState(null);useEffect(() => {let cancelled = false;// 定时器清理const timer = setInterval(() => {if (!cancelled) {// 更新数据}}, 1000);// 事件监听器清理const handleResize = () => {if (!cancelled) {// 处理resize}};window.addEventListener('resize', handleResize);// 清理函数return () => {cancelled = true;clearInterval(timer);window.removeEventListener('resize', handleResize);};}, []);return <div>{data}</div>;
}

9.3 条件渲染技巧

function ConditionalRendering({ user, loading, error }) {// ✅ 使用早期返回if (loading) {return <LoadingSpinner />;}if (error) {return <ErrorMessage error={error} />;}if (!user) {return <EmptyState message="用户不存在" />;}return (<div>{/* ✅ 使用逻辑与进行条件渲染 */}{user.isVip && <VipBadge />}{/* ✅ 使用三元运算符 */}<div className={user.isActive ? 'active' : 'inactive'}>{user.name}</div>{/* ❌ 避免使用 && 运算符和falsy值 */}{user.posts.length && <PostList posts={user.posts} />}{/* ✅ 正确的写法 */}{user.posts.length > 0 && <PostList posts={user.posts} />}</div>);
}

10. 调试技巧

10.1 React开发者工具

// 为组件添加displayName便于调试
const UserCard = memo(function UserCard(props) {return <div>{/* ... */}</div>;
});
UserCard.displayName = 'UserCard';// 使用React.StrictMode检测问题
function App() {return (<React.StrictMode><MyApp /></React.StrictMode>);
}

10.2 自定义Hook调试

import { useDebugValue } from 'react';function useCustomHook(value) {const [state, setState] = useState(value);// 在开发者工具中显示调试信息useDebugValue(state, state => `当前值: ${state}`);return [state, setState];
}

React组件是构建现代前端应用的核心,掌握这些概念、模式和最佳实践将帮助你编写更高质量、更可维护的React应用。记住要保持组件简单、可预测,并始终考虑性能和用户体验。

http://www.dtcms.com/a/511181.html

相关文章:

  • TypeScript:npm的types、typings、@type的区别
  • 我的第一份开源贡献:小米工程师程赛的社区之旅
  • Python 基础 | 第八课:函数详解与应用
  • 火狐浏览器替换js脚本
  • 车载诊断架构 --- 由一个售后问题引发对P4时间的思考
  • 第3章 SQL数据定义语句
  • phpcms 网站m8 wordpress主题
  • Docker到Kubernetes的平滑迁移(服务网格实战)
  • 数据挖掘知识体系分析
  • 简述网站建设的五类成员做电商网站公司
  • 数据结构——邻接表
  • 预算系统 - 项目优化点
  • 【软考备考】论软件架构设计-范文示例
  • 探讨一下java将来未来两年内的就业以及发展
  • [特殊字符] 已发布目标检测数据集合集(持续更新)
  • mysql主从延迟
  • 乌当区城乡建设局网站wordpress小工具下载
  • 网站后台不能上传2345应用商店
  • HTTPS与HTPP的区别
  • 链式结构二叉树:结点定义、创建及全操作实现(遍历 / 计数 / 销毁 / 判断完全二叉树)
  • android在sd卡中可以mkdir, 但是不可以createNewFile
  • 高性能小型国产铷原子钟:精准计时领域的 “定海神针​,铷钟,国产铷钟,模块原子钟,
  • 【JVM】基础概念之为什么要使用JVM
  • 【图像处理】灰度图像与二值化
  • Java常用工具类处理方法100例
  • 自己做的网页怎么上传网站吗营销型网站公司排名
  • FPGA强化-基于rom的vga图像显示
  • 越南语OCR——从图像识别到业务赋能的深度解析
  • Java 注解与反射实战:自定义注解从入门到精通
  • Ubuntu18.04 D435i RGB相机与IMU标定详细版(四)