2025年每个开发者都应该知道的10个React最佳实践
概述
_嘿,DEV社区!👋 React正在快速发展,跟上最佳实践是编写干净、高效和可维护代码的关键。_
✅ 1) 保持组件小而专注
一个组件理想情况下应该做好一件事。大型的"上帝组件"难以测试和维护。将大组件拆分为更小、可重用的组件。
🚫 不良实践
- // 一个做太多事情的组件
- function UserDashboard() {
- const [users, setUsers] = useState([]);
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState(null);
- const [filters, setFilters] = useState({});
- const [pagination, setPagination] = useState({ page: 1, limit: 10 });
- // 用户管理逻辑
- const fetchUsers = async () => { /* ... */ };
- const createUser = async () => { /* ... */ };
- const updateUser = async () => { /* ... */ };
- const deleteUser = async () => { /* ... */ };
- // 过滤和分页逻辑
- const applyFilters = () => { /* ... */ };
- const handlePageChange = () => { /* ... */ };
- // 复杂的渲染逻辑
- return (
- <div>
- {/* 用户列表 */}
- {/* 用户表单 */}
- {/* 过滤器 */}
- {/* 分页 */}
- {/* 统计信息 */}
- div>
- );
- }
✅ 清洁实践
- // 拆分为多个专注的组件
- function UserDashboard() {
- return (
- <div>
- <UserFilters />
- <UserList />
- <UserPagination />
- <UserStats />
- div>
- );
- }
- function UserList() {
- const [users, setUsers] = useState([]);
- const [loading, setLoading] = useState(false);
- const fetchUsers = async () => { /* ... */ };
- return (
- <div>
- {loading ? <LoadingSpinner /> :
>} - div>
- );
- }
✅ 2) 使用函数组件和Hooks
对于大多数用例,类组件已经过时。拥抱带有hooks的函数组件来处理状态、副作用等——它们更简单、更简洁。
🚫 类组件(过时)
- class Counter extends React.Component {
- constructor(props) {
- super(props);
- this.state = { count: 0 };
- }
- increment = () => {
- this.setState(prevState => ({ count: prevState.count + 1 }));
- };
- render() {
- return (
- <button onClick={this.increment}>
- Count: {this.state.count}
- button>
- );
- }
- }
✅ 函数组件和Hooks
- function Counter() {
- const [count, setCount] = useState(0);
- return (
- <button onClick={() => setCount(count + 1)}>
- Count: {count}
- button>
- );
- }
✅ 3) 解构Props和State
避免到处使用props.someValue。相反,解构props和state以获得更干净、更可读的代码:
🚫 不良实践
- function Welcome(props) {
- return <h1>Hello, {props.name}!h1>;
- }
- function UserProfile(props) {
- return (
- <div>
- <h2>{props.user.name}h2>
- <p>{props.user.email}p>
- <span>{props.user.role}span>
- div>
- );
- }
✅ 清洁实践
- function Welcome({ name }) {
- return <h1>Hello, {name}!h1>;
- }
- function UserProfile({ user: { name, email, role } }) {
- return (
- <div>
- <h2>{name}h2>
- <p>{email}p>
- <span>{role}span>
- div>
- );
- }
✅ 4) 保持JSX可读
长而深度嵌套的JSX难以阅读。用辅助函数或子组件来分解它。
🚫 不良实践
- function ProductCard({ product }) {
- return (
- <div className="product-card">
- <div className="product-image">
- <img src={product.image} alt={product.name} />
- {product.isNew && <span className="badge">Newspan>}
- {product.discount > 0 && (
- <span className="discount-badge">
- -{product.discount}%
- span>
- )}
- div>
- <div className="product-info">
- <h3>{product.name}h3>
- <p>{product.description}p>
- <div className="price-section">
- <span className="current-price">${product.price}span>
- {product.originalPrice > product.price && (
- <span className="original-price">
- ${product.originalPrice}
- span>
- )}
- div>
- <div className="actions">
- <button className="add-to-cart">Add to Cartbutton>
- <button className="wishlist">❤button>
- div>
- div>
- div>
- );
- }
✅ 清洁实践
- function ProductCard({ product }) {
- return (
- <div className="product-card">
- <ProductImage product={product} />
- <ProductInfo product={product} />
- <ProductActions product={product} />
- div>
- );
- }
- function ProductImage({ product }) {
- return (
- <div className="product-image">
- <img src={product.image} alt={product.name} />
- {product.isNew && <Badge type="new" />}
- {product.discount > 0 && (
- <Badge type="discount" value={product.discount} />
- )}
- div>
- );
- }
- function ProductInfo({ product }) {
- return (
- <div className="product-info">
- <h3>{product.name}h3>
- <p>{product.description}p>
- <PriceDisplay product={product} />
- div>
- );
- }
- function ProductActions({ product }) {
- return (
- <div className="actions">
- <button className="add-to-cart">Add to Cartbutton>
- <button className="wishlist">❤button>
- div>
- );
- }
✅ 5) 使用PropTypes或TypeScript
始终使用PropTypes验证组件props,或者更好的是,迁移到TypeScript以获得更安全、自文档化的代码。
使用PropTypes
- import PropTypes from 'prop-types';
- function UserCard({ user, onEdit, onDelete }) {
- return (
- <div className="user-card">
- <h3>{user.name}h3>
- <p>{user.email}p>
- <div className="actions">
- <button onClick={() => onEdit(user.id)}>Editbutton>
- <button onClick={() => onDelete(user.id)}>Deletebutton>
- div>
- div>
- );
- }
- UserCard.propTypes = {
- user: PropTypes.shape({
- id: PropTypes.number.isRequired,
- name: PropTypes.string.isRequired,
- email: PropTypes.string.isRequired,
- }).isRequired,
- onEdit: PropTypes.func.isRequired,
- onDelete: PropTypes.func.isRequired,
- };
使用TypeScript
- interface User {
- id: number;
- name: string;
- email: string;
- }
- interface UserCardProps {
- user: User;
- onEdit: (id: number) => void;
- onDelete: (id: number) => void;
- }
- function UserCard({ user, onEdit, onDelete }: UserCardProps) {
- return (
- <div className="user-card">
- <h3>{user.name}h3>
- <p>{user.email}p>
- <div className="actions">
- <button onClick={() => onEdit(user.id)}>Editbutton>
- <button onClick={() => onDelete(user.id)}>Deletebutton>
- div>
- div>
- );
- }
✅ 6) 利用React DevTools
在浏览器中使用React Developer Tools来检查组件树、props和state——它将为您节省数小时调试棘手问题的时间。
使用DevTools的技巧
组件树检查:查看组件层次结构
Props和State检查:实时查看数据流
性能分析:识别不必要的重新渲染
Profiler:分析组件渲染时间
✅ 7) 记忆化昂贵操作
使用React.memo、useMemo和useCallback避免不必要的重新渲染,特别是对于大型列表或密集计算。
使用React.memo
- const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
- // 昂贵的计算
- const processedData = data.map(item => ({
- ...item,
- processed: heavyComputation(item)
- }));
- return (
- <div>
- {processedData.map(item => (
- <div key={item.id}>{item.processed}div>
- ))}
- div>
- );
- });
使用useMemo
- function ProductList({ products, filters }) {
- const filteredProducts = useMemo(() => {
- return products.filter(product => {
- return filters.category === 'all' ||
- product.category === filters.category;
- });
- }, [products, filters.category]);
- const sortedProducts = useMemo(() => {
- return [...filteredProducts].sort((a, b) => {
- if (filters.sortBy === 'price') {
- return a.price - b.price;
- }
- return a.name.localeCompare(b.name);
- });
- }, [filteredProducts, filters.sortBy]);
- return (
- <div>
- {sortedProducts.map(product => (
- <ProductCard key={product.id} product={product} />
- ))}
- div>
- );
- }
使用useCallback
- function ParentComponent() {
- const [count, setCount] = useState(0);
- const handleClick = useCallback(() => {
- console.log('Button clicked');
- }, []); // 空依赖数组,函数永远不会改变
- return (
- <div>
- <p>Count: {count}p>
- <button onClick={() => setCount(count + 1)}>
- Increment
- button>
- <ChildComponent onAction={handleClick} />
- div>
- );
- }
- const ChildComponent = React.memo(function ChildComponent({ onAction }) {
- return <button onClick={onAction}>Child Actionbutton>;
- });
✅ 8) 清理副作用
使用useEffect时,始终清理订阅、定时器或事件监听器以防止内存泄漏。
正确的useEffect使用
- function Timer() {
- const [time, setTime] = useState(new Date());
- useEffect(() => {
- const id = setInterval(() => {
- setTime(new Date());
- }, 1000);
- return () => clearInterval(id); // 清理!
- }, []);
- return <div>Current time: {time.toLocaleTimeString()}div>;
- }
事件监听器清理
- function WindowResizeHandler() {
- const [windowSize, setWindowSize] = useState({
- width: window.innerWidth,
- height: window.innerHeight
- });
- useEffect(() => {
- const handleResize = () => {
- setWindowSize({
- width: window.innerWidth,
- height: window.innerHeight
- });
- };
- window.addEventListener('resize', handleResize);
- return () => {
- window.removeEventListener('resize', handleResize);
- };
- }, []);
- return (
- <div>
- Window size: {windowSize.width} x {windowSize.height}
- div>
- );
- }
API订阅清理
- function UserProfile({ userId }) {
- const [user, setUser] = useState(null);
- const [loading, setLoading] = useState(true);
- useEffect(() => {
- let isMounted = true;
- const fetchUser = async () => {
- try {
- const response = await fetch(`/api/users/${userId}`);
- const userData = await response.json();
- if (isMounted) {
- setUser(userData);
- setLoading(false);
- }
- } catch (error) {
- if (isMounted) {
- console.error('Failed to fetch user:', error);
- setLoading(false);
- }
- }
- };
- fetchUser();
- return () => {
- isMounted = false; // 防止在组件卸载后设置状态
- };
- }, [userId]);
- if (loading) return <div>Loading...div>;
- if (!user) return <div>User not founddiv>;
- return (
- <div>
- <h2>{user.name}h2>
- <p>{user.email}p>
- div>
- );
- }
✅ 9) 尽可能保持状态本地
不要不必要地提升状态。本地状态减少复杂性和重新渲染,使您的组件更快、更容易维护。
🚫 不必要的状态提升
- // 父组件管理所有状态
- function ParentComponent() {
- const [child1Data, setChild1Data] = useState('');
- const [child2Data, setChild2Data] = useState('');
- const [child3Data, setChild3Data] = useState('');
- return (
- <div>
- <Child1 data={child1Data} setData={setChild1Data} />
- <Child2 data={child2Data} setData={setChild2Data} />
- <Child3 data={child3Data} setData={setChild3Data} />
- div>
- );
- }
✅ 本地状态管理
- // 每个组件管理自己的状态
- function ParentComponent() {
- return (
- <div>
- <Child1 />
- <Child2 />
- <Child3 />
- div>
- );
- }
- function Child1() {
- const [data, setData] = useState('');
- // 只在需要时使用本地状态
- return <input value={data} onChange={e => setData(e.target.value)} />;
- }
何时提升状态
- // 当多个组件需要共享状态时
- function TodoApp() {
- const [todos, setTodos] = useState([]);
- const addTodo = (text) => {
- setTodos([...todos, { id: Date.now(), text, completed: false }]);
- };
- const toggleTodo = (id) => {
- setTodos(todos.map(todo =>
- todo.id === id ? { ...todo, completed: !todo.completed } : todo
- ));
- };
- return (
- <div>
- <TodoForm onAdd={addTodo} />
- <TodoList todos={todos} onToggle={toggleTodo} />
- <TodoStats todos={todos} />
- div>
- );
- }
✅ 10) 使用有意义的命名
始终为组件、props和hooks使用清晰、描述性的名称。像Button、handleClick或isLoading这样的名称使您的代码自解释,更容易被他人理解。
🚫 不良命名
- function Comp({ d, f }) {
- const [l, setL] = useState(false);
- const hc = () => {
- setL(true);
- f(d);
- };
- return <button onClick={hc}>Clickbutton>;
- }
✅ 有意义的命名
- function UserButton({ user, onUserClick }) {
- const [isLoading, setIsLoading] = useState(false);
- const handleClick = () => {
- setIsLoading(true);
- onUserClick(user);
- };
- return (
- <button
- onClick={handleClick}
- disabled={isLoading}
- >
- {isLoading ? 'Loading...' : 'View Profile'}
- button>
- );
- }
命名约定
- // 组件:PascalCase
- function UserProfile() { }
- function ProductCard() { }
- function NavigationMenu() { }
- // Props:camelCase
- function Button({ onClick, isDisabled, children }) { }
- // 事件处理函数:handle + 事件名
- function Form() {
- const handleSubmit = () => { };
- const handleInputChange = () => { };
- const handleButtonClick = () => { };
- }
- // 布尔props:is/has/can前缀
- function Modal({ isOpen, hasOverlay, canClose }) { }
- // 状态:描述性名称
- const [isLoading, setIsLoading] = useState(false);
- const [userData, setUserData] = useState(null);
- const [errorMessage, setErrorMessage] = useState('');
实际应用示例
1. 完整的用户管理组件
- // 主组件
- function UserManagement() {
- return (
- <div className="user-management">
- <UserHeader />
- <UserFilters />
- <UserList />
- <UserPagination />
- div>
- );
- }
- // 用户列表组件
- function UserList() {
- const [users, setUsers] = useState([]);
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState(null);
- const fetchUsers = useCallback(async () => {
- setLoading(true);
- try {
- const response = await fetch('/api/users');
- const data = await response.json();
- setUsers(data);
- } catch (err) {
- setError(err.message);
- } finally {
- setLoading(false);
- }
- }, []);
- useEffect(() => {
- fetchUsers();
- }, [fetchUsers]);
- if (loading) return <LoadingSpinner />;
- if (error) return <ErrorMessage message={error} />;
- return (
- <div className="user-list">
- {users.map(user => (
- <UserCard key={user.id} user={user} />
- ))}
- div>
- );
- }
- // 用户卡片组件
- const UserCard = React.memo(function UserCard({ user }) {
- const [isEditing, setIsEditing] = useState(false);
- const handleEdit = useCallback(() => {
- setIsEditing(true);
- }, []);
- const handleSave = useCallback(async (updatedUser) => {
- try {
- await updateUser(updatedUser);
- setIsEditing(false);
- } catch (error) {
- console.error('Failed to update user:', error);
- }
- }, []);
- return (
- <div className="user-card">
- {isEditing ? (
- <UserEditForm
- user={user}
- onSave={handleSave}
- onCancel={() => setIsEditing(false)}
- />
- ) : (
- <UserDisplay
- user={user}
- onEdit={handleEdit}
- />
- )}
- div>
- );
- });
2. 自定义Hook示例
- // 自定义Hook:数据获取
- function useDataFetching(url) {
- const [data, setData] = useState(null);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState(null);
- useEffect(() => {
- let isMounted = true;
- const fetchData = async () => {
- try {
- setLoading(true);
- const response = await fetch(url);
- const result = await response.json();
- if (isMounted) {
- setData(result);
- setError(null);
- }
- } catch (err) {
- if (isMounted) {
- setError(err.message);
- }
- } finally {
- if (isMounted) {
- setLoading(false);
- }
- }
- };
- fetchData();
- return () => {
- isMounted = false;
- };
- }, [url]);
- return { data, loading, error };
- }
- // 使用自定义Hook
- function ProductList() {
- const { data: products, loading, error } = useDataFetching('/api/products');
- if (loading) return <LoadingSpinner />;
- if (error) return <ErrorMessage message={error} />;
- return (
- <div className="product-list">
- {products.map(product => (
- <ProductCard key={product.id} product={product} />
- ))}
- div>
- );
- }
总结
💡记住:小组件 + hooks + 有意义的命名 = 快乐的开发者和可维护的应用!🎉
这些最佳实践将帮助您:
编写更干净、更可维护的代码
提高应用性能
减少bug和调试时间
改善团队协作
