2025年每个开发者都应该知道的10个React最佳实践

概述
_嘿,DEV社区!👋 React正在快速发展,跟上最佳实践是编写干净、高效和可维护代码的关键。_
1) 保持组件小而专注
一个组件理想情况下应该做好一件事。大型的"上帝组件"难以测试和维护。将大组件拆分为更小、可重用的组件。
🚫 不良实践
  1. // 一个做太多事情的组件
  2. function UserDashboard() {
  3. const [users, setUsers] = useState([]);
  4. const [loading, setLoading] = useState(false);
  5. const [error, setError] = useState(null);
  6. const [filters, setFilters] = useState({});
  7. const [pagination, setPagination] = useState({ page: 1, limit: 10 });
  8. // 用户管理逻辑
  9. const fetchUsers = async () => { /* ... */ };
  10. const createUser = async () => { /* ... */ };
  11. const updateUser = async () => { /* ... */ };
  12. const deleteUser = async () => { /* ... */ };
  13. // 过滤和分页逻辑
  14. const applyFilters = () => { /* ... */ };
  15. const handlePageChange = () => { /* ... */ };
  16. // 复杂的渲染逻辑
  17. return (
  18. <div>
  19. {/* 用户列表 */}
  20. {/* 用户表单 */}
  21. {/* 过滤器 */}
  22. {/* 分页 */}
  23. {/* 统计信息 */}
  24. div>
  25. );
  26. }
jsx
清洁实践
  1. // 拆分为多个专注的组件
  2. function UserDashboard() {
  3. return (
  4. <div>
  5. <UserFilters />
  6. <UserList />
  7. <UserPagination />
  8. <UserStats />
  9. div>
  10. );
  11. }

  12. function UserList() {
  13. const [users, setUsers] = useState([]);
  14. const [loading, setLoading] = useState(false);
  15. const fetchUsers = async () => { /* ... */ };
  16. return (
  17. <div>
  18. {loading ? <LoadingSpinner /> : >}
  19. div>
  20. );
  21. }
jsx
2) 使用函数组件和Hooks
对于大多数用例,类组件已经过时。拥抱带有hooks的函数组件来处理状态、副作用等——它们更简单、更简洁。
🚫 类组件(过时)
  1. class Counter extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = { count: 0 };
  5. }
  6. increment = () => {
  7. this.setState(prevState => ({ count: prevState.count + 1 }));
  8. };
  9. render() {
  10. return (
  11. <button onClick={this.increment}>
  12. Count: {this.state.count}
  13. button>
  14. );
  15. }
  16. }
jsx
函数组件和Hooks
  1. function Counter() {
  2. const [count, setCount] = useState(0);
  3. return (
  4. <button onClick={() => setCount(count + 1)}>
  5. Count: {count}
  6. button>
  7. );
  8. }
jsx
3) 解构Props和State
避免到处使用props.someValue。相反,解构props和state以获得更干净、更可读的代码:
🚫 不良实践
  1. function Welcome(props) {
  2. return <h1>Hello, {props.name}!h1>;
  3. }

  4. function UserProfile(props) {
  5. return (
  6. <div>
  7. <h2>{props.user.name}h2>
  8. <p>{props.user.email}p>
  9. <span>{props.user.role}span>
  10. div>
  11. );
  12. }
jsx
清洁实践
  1. function Welcome({ name }) {
  2. return <h1>Hello, {name}!h1>;
  3. }

  4. function UserProfile({ user: { name, email, role } }) {
  5. return (
  6. <div>
  7. <h2>{name}h2>
  8. <p>{email}p>
  9. <span>{role}span>
  10. div>
  11. );
  12. }
jsx
4) 保持JSX可读
长而深度嵌套的JSX难以阅读。用辅助函数或子组件来分解它。
🚫 不良实践
  1. function ProductCard({ product }) {
  2. return (
  3. <div className="product-card">
  4. <div className="product-image">
  5. <img src={product.image} alt={product.name} />
  6. {product.isNew && <span className="badge">Newspan>}
  7. {product.discount > 0 && (
  8. <span className="discount-badge">
  9. -{product.discount}%
  10. span>
  11. )}
  12. div>
  13. <div className="product-info">
  14. <h3>{product.name}h3>
  15. <p>{product.description}p>
  16. <div className="price-section">
  17. <span className="current-price">${product.price}span>
  18. {product.originalPrice > product.price && (
  19. <span className="original-price">
  20. ${product.originalPrice}
  21. span>
  22. )}
  23. div>
  24. <div className="actions">
  25. <button className="add-to-cart">Add to Cartbutton>
  26. <button className="wishlist">❤button>
  27. div>
  28. div>
  29. div>
  30. );
  31. }
jsx
清洁实践
  1. function ProductCard({ product }) {
  2. return (
  3. <div className="product-card">
  4. <ProductImage product={product} />
  5. <ProductInfo product={product} />
  6. <ProductActions product={product} />
  7. div>
  8. );
  9. }

  10. function ProductImage({ product }) {
  11. return (
  12. <div className="product-image">
  13. <img src={product.image} alt={product.name} />
  14. {product.isNew && <Badge type="new" />}
  15. {product.discount > 0 && (
  16. <Badge type="discount" value={product.discount} />
  17. )}
  18. div>
  19. );
  20. }

  21. function ProductInfo({ product }) {
  22. return (
  23. <div className="product-info">
  24. <h3>{product.name}h3>
  25. <p>{product.description}p>
  26. <PriceDisplay product={product} />
  27. div>
  28. );
  29. }

  30. function ProductActions({ product }) {
  31. return (
  32. <div className="actions">
  33. <button className="add-to-cart">Add to Cartbutton>
  34. <button className="wishlist">❤button>
  35. div>
  36. );
  37. }
jsx
5) 使用PropTypes或TypeScript
始终使用PropTypes验证组件props,或者更好的是,迁移到TypeScript以获得更安全、自文档化的代码。
使用PropTypes
  1. import PropTypes from 'prop-types';

  2. function UserCard({ user, onEdit, onDelete }) {
  3. return (
  4. <div className="user-card">
  5. <h3>{user.name}h3>
  6. <p>{user.email}p>
  7. <div className="actions">
  8. <button onClick={() => onEdit(user.id)}>Editbutton>
  9. <button onClick={() => onDelete(user.id)}>Deletebutton>
  10. div>
  11. div>
  12. );
  13. }

  14. UserCard.propTypes = {
  15. user: PropTypes.shape({
  16. id: PropTypes.number.isRequired,
  17. name: PropTypes.string.isRequired,
  18. email: PropTypes.string.isRequired,
  19. }).isRequired,
  20. onEdit: PropTypes.func.isRequired,
  21. onDelete: PropTypes.func.isRequired,
  22. };
jsx
使用TypeScript
  1. interface User {
  2. id: number;
  3. name: string;
  4. email: string;
  5. }

  6. interface UserCardProps {
  7. user: User;
  8. onEdit: (id: number) => void;
  9. onDelete: (id: number) => void;
  10. }

  11. function UserCard({ user, onEdit, onDelete }: UserCardProps) {
  12. return (
  13. <div className="user-card">
  14. <h3>{user.name}h3>
  15. <p>{user.email}p>
  16. <div className="actions">
  17. <button onClick={() => onEdit(user.id)}>Editbutton>
  18. <button onClick={() => onDelete(user.id)}>Deletebutton>
  19. div>
  20. div>
  21. );
  22. }
tsx
6) 利用React DevTools
在浏览器中使用React Developer Tools来检查组件树、props和state——它将为您节省数小时调试棘手问题的时间。
使用DevTools的技巧
组件树检查:查看组件层次结构
Props和State检查:实时查看数据流
性能分析:识别不必要的重新渲染
Profiler:分析组件渲染时间
7) 记忆化昂贵操作
使用React.memo、useMemo和useCallback避免不必要的重新渲染,特别是对于大型列表或密集计算。
使用React.memo
  1. const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
  2. // 昂贵的计算
  3. const processedData = data.map(item => ({
  4. ...item,
  5. processed: heavyComputation(item)
  6. }));
  7. return (
  8. <div>
  9. {processedData.map(item => (
  10. <div key={item.id}>{item.processed}div>
  11. ))}
  12. div>
  13. );
  14. });
jsx
使用useMemo
  1. function ProductList({ products, filters }) {
  2. const filteredProducts = useMemo(() => {
  3. return products.filter(product => {
  4. return filters.category === 'all' ||
  5. product.category === filters.category;
  6. });
  7. }, [products, filters.category]);
  8. const sortedProducts = useMemo(() => {
  9. return [...filteredProducts].sort((a, b) => {
  10. if (filters.sortBy === 'price') {
  11. return a.price - b.price;
  12. }
  13. return a.name.localeCompare(b.name);
  14. });
  15. }, [filteredProducts, filters.sortBy]);
  16. return (
  17. <div>
  18. {sortedProducts.map(product => (
  19. <ProductCard key={product.id} product={product} />
  20. ))}
  21. div>
  22. );
  23. }
jsx
使用useCallback
  1. function ParentComponent() {
  2. const [count, setCount] = useState(0);
  3. const handleClick = useCallback(() => {
  4. console.log('Button clicked');
  5. }, []); // 空依赖数组,函数永远不会改变
  6. return (
  7. <div>
  8. <p>Count: {count}p>
  9. <button onClick={() => setCount(count + 1)}>
  10. Increment
  11. button>
  12. <ChildComponent onAction={handleClick} />
  13. div>
  14. );
  15. }

  16. const ChildComponent = React.memo(function ChildComponent({ onAction }) {
  17. return <button onClick={onAction}>Child Actionbutton>;
  18. });
jsx
8) 清理副作用
使用useEffect时,始终清理订阅、定时器或事件监听器以防止内存泄漏。
正确的useEffect使用
  1. function Timer() {
  2. const [time, setTime] = useState(new Date());
  3. useEffect(() => {
  4. const id = setInterval(() => {
  5. setTime(new Date());
  6. }, 1000);
  7. return () => clearInterval(id); // 清理!
  8. }, []);
  9. return <div>Current time: {time.toLocaleTimeString()}div>;
  10. }
jsx
事件监听器清理
  1. function WindowResizeHandler() {
  2. const [windowSize, setWindowSize] = useState({
  3. width: window.innerWidth,
  4. height: window.innerHeight
  5. });
  6. useEffect(() => {
  7. const handleResize = () => {
  8. setWindowSize({
  9. width: window.innerWidth,
  10. height: window.innerHeight
  11. });
  12. };
  13. window.addEventListener('resize', handleResize);
  14. return () => {
  15. window.removeEventListener('resize', handleResize);
  16. };
  17. }, []);
  18. return (
  19. <div>
  20. Window size: {windowSize.width} x {windowSize.height}
  21. div>
  22. );
  23. }
jsx
API订阅清理
  1. function UserProfile({ userId }) {
  2. const [user, setUser] = useState(null);
  3. const [loading, setLoading] = useState(true);
  4. useEffect(() => {
  5. let isMounted = true;
  6. const fetchUser = async () => {
  7. try {
  8. const response = await fetch(`/api/users/${userId}`);
  9. const userData = await response.json();
  10. if (isMounted) {
  11. setUser(userData);
  12. setLoading(false);
  13. }
  14. } catch (error) {
  15. if (isMounted) {
  16. console.error('Failed to fetch user:', error);
  17. setLoading(false);
  18. }
  19. }
  20. };
  21. fetchUser();
  22. return () => {
  23. isMounted = false; // 防止在组件卸载后设置状态
  24. };
  25. }, [userId]);
  26. if (loading) return <div>Loading...div>;
  27. if (!user) return <div>User not founddiv>;
  28. return (
  29. <div>
  30. <h2>{user.name}h2>
  31. <p>{user.email}p>
  32. div>
  33. );
  34. }
jsx
9) 尽可能保持状态本地
不要不必要地提升状态。本地状态减少复杂性和重新渲染,使您的组件更快、更容易维护。
🚫 不必要的状态提升
  1. // 父组件管理所有状态
  2. function ParentComponent() {
  3. const [child1Data, setChild1Data] = useState('');
  4. const [child2Data, setChild2Data] = useState('');
  5. const [child3Data, setChild3Data] = useState('');
  6. return (
  7. <div>
  8. <Child1 data={child1Data} setData={setChild1Data} />
  9. <Child2 data={child2Data} setData={setChild2Data} />
  10. <Child3 data={child3Data} setData={setChild3Data} />
  11. div>
  12. );
  13. }
jsx
本地状态管理
  1. // 每个组件管理自己的状态
  2. function ParentComponent() {
  3. return (
  4. <div>
  5. <Child1 />
  6. <Child2 />
  7. <Child3 />
  8. div>
  9. );
  10. }

  11. function Child1() {
  12. const [data, setData] = useState('');
  13. // 只在需要时使用本地状态
  14. return <input value={data} onChange={e => setData(e.target.value)} />;
  15. }
jsx
何时提升状态
  1. // 当多个组件需要共享状态时
  2. function TodoApp() {
  3. const [todos, setTodos] = useState([]);
  4. const addTodo = (text) => {
  5. setTodos([...todos, { id: Date.now(), text, completed: false }]);
  6. };
  7. const toggleTodo = (id) => {
  8. setTodos(todos.map(todo =>
  9. todo.id === id ? { ...todo, completed: !todo.completed } : todo
  10. ));
  11. };
  12. return (
  13. <div>
  14. <TodoForm onAdd={addTodo} />
  15. <TodoList todos={todos} onToggle={toggleTodo} />
  16. <TodoStats todos={todos} />
  17. div>
  18. );
  19. }
jsx
10) 使用有意义的命名
始终为组件、props和hooks使用清晰、描述性的名称。像Button、handleClick或isLoading这样的名称使您的代码自解释,更容易被他人理解。
🚫 不良命名
  1. function Comp({ d, f }) {
  2. const [l, setL] = useState(false);
  3. const hc = () => {
  4. setL(true);
  5. f(d);
  6. };
  7. return <button onClick={hc}>Clickbutton>;
  8. }
jsx
有意义的命名
  1. function UserButton({ user, onUserClick }) {
  2. const [isLoading, setIsLoading] = useState(false);
  3. const handleClick = () => {
  4. setIsLoading(true);
  5. onUserClick(user);
  6. };
  7. return (
  8. <button
  9. onClick={handleClick}
  10. disabled={isLoading}
  11. >
  12. {isLoading ? 'Loading...' : 'View Profile'}
  13. button>
  14. );
  15. }
jsx
命名约定
  1. // 组件:PascalCase
  2. function UserProfile() { }
  3. function ProductCard() { }
  4. function NavigationMenu() { }

  5. // Props:camelCase
  6. function Button({ onClick, isDisabled, children }) { }

  7. // 事件处理函数:handle + 事件名
  8. function Form() {
  9. const handleSubmit = () => { };
  10. const handleInputChange = () => { };
  11. const handleButtonClick = () => { };
  12. }

  13. // 布尔props:is/has/can前缀
  14. function Modal({ isOpen, hasOverlay, canClose }) { }

  15. // 状态:描述性名称
  16. const [isLoading, setIsLoading] = useState(false);
  17. const [userData, setUserData] = useState(null);
  18. const [errorMessage, setErrorMessage] = useState('');
jsx
实际应用示例
1. 完整的用户管理组件
  1. // 主组件
  2. function UserManagement() {
  3. return (
  4. <div className="user-management">
  5. <UserHeader />
  6. <UserFilters />
  7. <UserList />
  8. <UserPagination />
  9. div>
  10. );
  11. }

  12. // 用户列表组件
  13. function UserList() {
  14. const [users, setUsers] = useState([]);
  15. const [loading, setLoading] = useState(false);
  16. const [error, setError] = useState(null);
  17. const fetchUsers = useCallback(async () => {
  18. setLoading(true);
  19. try {
  20. const response = await fetch('/api/users');
  21. const data = await response.json();
  22. setUsers(data);
  23. } catch (err) {
  24. setError(err.message);
  25. } finally {
  26. setLoading(false);
  27. }
  28. }, []);
  29. useEffect(() => {
  30. fetchUsers();
  31. }, [fetchUsers]);
  32. if (loading) return <LoadingSpinner />;
  33. if (error) return <ErrorMessage message={error} />;
  34. return (
  35. <div className="user-list">
  36. {users.map(user => (
  37. <UserCard key={user.id} user={user} />
  38. ))}
  39. div>
  40. );
  41. }

  42. // 用户卡片组件
  43. const UserCard = React.memo(function UserCard({ user }) {
  44. const [isEditing, setIsEditing] = useState(false);
  45. const handleEdit = useCallback(() => {
  46. setIsEditing(true);
  47. }, []);
  48. const handleSave = useCallback(async (updatedUser) => {
  49. try {
  50. await updateUser(updatedUser);
  51. setIsEditing(false);
  52. } catch (error) {
  53. console.error('Failed to update user:', error);
  54. }
  55. }, []);
  56. return (
  57. <div className="user-card">
  58. {isEditing ? (
  59. <UserEditForm
  60. user={user}
  61. onSave={handleSave}
  62. onCancel={() => setIsEditing(false)}
  63. />
  64. ) : (
  65. <UserDisplay
  66. user={user}
  67. onEdit={handleEdit}
  68. />
  69. )}
  70. div>
  71. );
  72. });
jsx
2. 自定义Hook示例
  1. // 自定义Hook:数据获取
  2. function useDataFetching(url) {
  3. const [data, setData] = useState(null);
  4. const [loading, setLoading] = useState(true);
  5. const [error, setError] = useState(null);
  6. useEffect(() => {
  7. let isMounted = true;
  8. const fetchData = async () => {
  9. try {
  10. setLoading(true);
  11. const response = await fetch(url);
  12. const result = await response.json();
  13. if (isMounted) {
  14. setData(result);
  15. setError(null);
  16. }
  17. } catch (err) {
  18. if (isMounted) {
  19. setError(err.message);
  20. }
  21. } finally {
  22. if (isMounted) {
  23. setLoading(false);
  24. }
  25. }
  26. };
  27. fetchData();
  28. return () => {
  29. isMounted = false;
  30. };
  31. }, [url]);
  32. return { data, loading, error };
  33. }

  34. // 使用自定义Hook
  35. function ProductList() {
  36. const { data: products, loading, error } = useDataFetching('/api/products');
  37. if (loading) return <LoadingSpinner />;
  38. if (error) return <ErrorMessage message={error} />;
  39. return (
  40. <div className="product-list">
  41. {products.map(product => (
  42. <ProductCard key={product.id} product={product} />
  43. ))}
  44. div>
  45. );
  46. }
jsx
总结
💡记住:小组件 + hooks + 有意义的命名 = 快乐的开发者和可维护的应用!🎉
这些最佳实践将帮助您:
编写更干净、更可维护的代码
提高应用性能
减少bug和调试时间
改善团队协作