mcpField
R
react-development

react-development

github.com/manutej开发62465 次安装40 周前更新
代码仓库

关于 react-development

面向 React 开发者的技能指南,依据 React 官方文档整理现代应用开发方法。内容覆盖组件、Hooks、状态管理、自定义 Hooks 和性能优化。

主要功能

  1. 01组件组合与 JSX
  2. 02常用 Hooks 与副作用
  3. 03Context 和 Reducer 状态管理
  4. 04可复用自定义 Hooks
  5. 05记忆化、懒加载与代码拆分

适用场景

  1. 01构建单页 React 应用
  2. 02实现表单与数据获取
  3. 03将类组件迁移到函数组件
  4. 04优化大型列表和应用性能

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/manutej/luxor-claude-marketplace --skill react-development

安装后在 Claude Code 中输入/react-development确认已加载。

附带文件EXAMPLES.mdREADME.md

详细介绍

React 开发技能

此技能基于 react.dev 上的 React 官方文档,全面介绍如何使用 hooks、组件、状态管理、context、effects 和性能优化技术来构建现代 React 应用。

何时使用此技能

在以下情况下使用此技能:

  • 使用 React 构建单页应用(SPAs)
  • 创建可复用的 UI 组件和组件库
  • 使用 hooks 和 context 管理复杂的应用状态
  • 实现表单、数据获取和副作用
  • 优化 React 应用的性能
  • 使用动态数据构建交互式用户界面
  • 将类组件迁移为使用 hooks 的函数组件
  • 无需外部库即可实现全局状态管理
  • 创建可复用逻辑的自定义 hooks
  • 构建无障碍且高性能的 Web 应用

核心概念

组件

组件是 React 应用的构建模块,可将 UI 拆分为独立、可复用的部分。

函数组件(现代方式):

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// Arrow function syntax
const Greeting = ({ name, age }) => {
  return (
    <div>
      <h2>{name}</h2>
      <p>Age: {age}</p>
    </div>
  );
};

组件组合:

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}
JSX

JSX 是 JavaScript 的一种语法扩展,外观类似 HTML,可生成 React 元素。

JSX 基础:

// Embedding expressions
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;

// JSX attributes
const image = <img src={user.avatarUrl} alt={user.name} />;

// JSX children
const container = (
  <div>
    <h1>Welcome</h1>
    <p>Get started with React</p>
  </div>
);

// Conditional rendering
const greeting = (
  <div>
    {isLoggedIn ? <UserGreeting /> : <GuestGreeting />}
  </div>
);

// Lists and keys
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li key={number.toString()}>{number}</li>
);
Props

Props 是传入 React 组件的参数,通过 HTML 属性传递给组件。

传递和使用 Props:

function Product({ name, price, inStock }) {
  return (
    <div className="product">
      <h3>{name}</h3>
      <p>${price}</p>
      {inStock ? <span>In Stock</span> : <span>Out of Stock</span>}
    </div>
  );
}

// Usage
<Product name="Laptop" price={999} inStock={true} />

带有 Children 的 Props:

function Card({ title, children }) {
  return (
    <div className="card">
      <h2>{title}</h2>
      <div className="card-content">
        {children}
      </div>
    </div>
  );
}

// Usage
<Card title="Welcome">
  <p>This is the card content</p>
  <button>Click me</button>
</Card>

默认 Props:

function Button({ text = 'Click me', variant = 'primary' }) {
  return <button className={variant}>{text}</button>;
}
State

State 是组件的记忆,让组件能够记住信息并响应用户交互。

组件本地状态:

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount(count - 1)}>Decrement</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}

React Hooks

Hooks 让你能够在函数组件中使用状态和其他 React 功能。

useState

useState hook 让你能够向函数组件添加状态。

基本用法:

import { useState } from 'react';

function Form() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log({ name, email });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name"
      />
      <input
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
        type="email"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

对象状态:

function UserProfile() {
  const [user, setUser] = useState({
    name: '',
    age: 0,
    email: ''
  });

  const updateField = (field, value) => {
    setUser(prev => ({
      ...prev,
      [field]: value
    }));
  };

  return (
    <div>
      <input
        value={user.name}
        onChange={(e) => updateField('name', e.target.value)}
      />
      <input
        type="number"
        value={user.age}
        onChange={(e) => updateField('age', parseInt(e.target.value))}
      />
      <input
        type="email"
        value={user.email}
        onChange={(e) => updateField('email', e.target.value)}
      />
    </div>
  );
}

数组状态:

function TodoList() {
  const [todos, setTodos] = useState([]);
  const [input, setInput] = useState('');

  const addTodo = () => {
    setTodos(prev => [...prev, { id: Date.now(), text: input }]);
    setInput('');
  };

  const removeTodo = (id) => {
    setTodos(prev => prev.filter(todo => todo.id !== id));
  };

  return (
    <div>
      <input value={input} onChange={(e) => setInput(e.target.value)} />
      <button onClick={addTodo}>Add</button>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            {todo.text}
            <button onClick={() => removeTodo(todo.id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}
useEffect

useEffect hook 让你能够在函数组件中执行副作用。

基本副作用:

import { useState, useEffect } from 'react';

function DocumentTitle() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  return (
    <button onClick={() => setCount(count + 1)}>
      Clicked {count} times
    </button>
  );
}

数据获取:

function UserData({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let cancelled = false;

    async function fetchUser() {
      try {
        setLoading(true);
        const response = await fetch(`/api/users/${userId}`);
        const data = await response.json();

        if (!cancelled) {
          setUser(data);
          setError(null);
        }
      } catch (err) {
        if (!cancelled) {
          setError(err.message);
        }
      } finally {
        if (!cancelled) {
          setLoading(false);
        }
      }
    }

    fetchUser();

    return () => {
      cancelled = true;
    };
  }, [userId]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return <div>{user?.name}</div>;
}

事件监听器和清理:

function WindowSize() {
  const [size, setSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight
  });

  useEffect(() => {
    function handleResize() {
      setSize({
        width: window.innerWidth,
        height: window.innerHeight
      });
    }

    window.addEventListener('resize', handleResize);

    // Cleanup function
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []); // Empty dependency array = run once on mount

  return <div>{size.width} x {size.height}</div>;
}

计时器和间隔:

function Timer() {
  const [seconds, setSeconds] = useState(0);
  const [isRunning, setIsRunning] = useState(false);

  useEffect(() => {
    if (!isRunning) return;

    const interval = setInterval(() => {
      setSeconds(s => s + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, [isRunning]);

  return (
    <div>
      <p>Seconds: {seconds}</p>
      <button onClick={() => setIsRunning(!isRunning)}>
        {isRunning ? 'Pause' : 'Start'}
      </button>
      <button onClick={() => setSeconds(0)}>Reset</button>
    </div>
  );
}
useContext

useContext hook 让你能够在组件中读取并订阅 context。

创建和使用 Context:

import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext('light');

function App() {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={theme}>
      <Toolbar />
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
        Toggle Theme
      </button>
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);

  return (
    <button className={theme}>
      I am styled by {theme} theme
    </button>
  );
}

多个 Context:

const ThemeContext = createContext('light');
const UserContext = createContext(null);

function App() {
  const [theme, setTheme] = useState('light');
  const [currentUser, setCurrentUser] = useState({ name: 'John', role: 'admin' });

  return (
    <ThemeContext.Provider value={theme}>
      <UserContext.Provider value={currentUser}>
        <Dashboard />
      </UserContext.Provider>
    </ThemeContext.Provider>
  );
}

function Dashboard() {
  const theme = useContext(ThemeContext);
  const user = useContext(UserContext);

  return (
    <div className={theme}>
      <h1>Welcome, {user.name}</h1>
      <p>Role: {user.role}</p>
    </div>
  );
}
useReducer

useReducer hook 是 useState 的替代方案,可用于管理复杂的状态逻辑。

基本 Reducer 模式:

import { useReducer } from 'react';

function counterReducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return { count: 0 };
    default:
      throw new Error(`Unknown action: ${action.type}`);
  }
}

function Counter() {
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
    </div>
  );
}

复杂状态管理(来自 Context7 的任务列表模式):

function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [...tasks, {
        id: action.id,
        text: action.text,
        done: false
      }];
    }
    case 'changed': {
      return tasks.map(t => {
        if (t.id === action.task.id) {
          return action.task;
        } else {
          return t;
        }
      });
    }
    case 'deleted': {
      return tasks.filter(t => t.id !== action.id);
    }
    default: {
      throw Error('Unknown action: ' + action.type);
    }
  }
}

function TaskApp() {
  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);

  function handleAddTask(text) {
    dispatch({
      type: 'added',
      id: nextId++,
      text: text,
    });
  }

  function handleChangeTask(task) {
    dispatch({
      type: 'changed',
      task: task
    });
  }

  function handleDeleteTask(taskId) {
    dispatch({
      type: 'deleted',
      id: taskId
    });
  }

  return (
    <>
      <h1>Prague itinerary</h1>
      <AddTask onAddTask={handleAddTask} />
      <TaskList
        tasks={tasks}
        onChangeTask={handleChangeTask}
        onDeleteTask={handleDeleteTask}
      />
    </>
  );
}

let nextId = 3;
const initialTasks = [
  { id: 0, text: 'Visit Kafka Museum', done: true },
  { id: 1, text: 'Watch a puppet show', done: false },
  { id: 2, text: 'Lennon Wall pic', done: false }
];
useMemo

useMemo hook 让你能够缓存开销较大的计算结果。

缓存开销较大的计算:

import { useMemo, useState } from 'react';

function ProductList({ products, category }) {
  const [sortOrder, setSortOrder] = useState('asc');

  const filteredAndSortedProducts = useMemo(() => {
    console.log('Filtering and sorting products...');

    const filtered = products.filter(p => p.category === category);

    return filtered.sort((a, b) => {
      if (sortOrder === 'asc') {
        return a.price - b.price;
      }
      return b.price - a.price;
    });
  }, [products, category, sortOrder]);

  return (
    <div>
      <button onClick={() => setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc')}>
        Sort: {sortOrder}
      </button>
      <ul>
        {filteredAndSortedProducts.map(product => (
          <li key={product.id}>{product.name} - ${product.price}</li>
        ))}
      </ul>
    </div>
  );
}

避免重新创建对象:

function SearchResults({ query }) {
  const searchOptions = useMemo(() => ({
    query,
    limit: 10,
    caseSensitive: false
  }), [query]);

  // searchOptions object only recreated when query changes
  const results = useSearch(searchOptions);

  return <ResultsList results={results} />;
}
useCallback

useCallback hook 让你能够在重新渲染之间缓存函数定义。

缓存事件处理函数:

import { useCallback, useState } from 'react';

function ProductPage({ productId }) {
  const [items, setItems] = useState([]);

  const handleAddToCart = useCallback(() => {
    setItems(prevItems => [...prevItems, productId]);
  }, [productId]);

  return <AddToCartButton onAdd={handleAddToCart} />;
}

// Memoized child component
const AddToCartButton = memo(({ onAdd }) => {
  console.log('Button rendered');
  return <button onClick={onAdd}>Add to Cart</button>;
});

优化子组件:

function TodoList() {
  const [todos, setTodos] = useState(initialTodos);

  const handleToggle = useCallback((id) => {
    setTodos(prevTodos =>
      prevTodos.map(todo =>
        todo.id === id ? { ...todo, done: !todo.done } : todo
      )
    );
  }, []);

  const handleDelete = useCallback((id) => {
    setTodos(prevTodos => prevTodos.filter(todo => todo.id !== id));
  }, []);

  return (
    <ul>
      {todos.map(todo => (
        <TodoItem
          key={todo.id}
          todo={todo}
          onToggle={handleToggle}
          onDelete={handleDelete}
        />
      ))}
    </ul>
  );
}
useRef

useRef hook 让你能够引用渲染时不需要用到的值。

访问 DOM 元素:

import { useRef } from 'react';

function TextInput() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>Focus input</button>
    </>
  );
}

存储可变值:

function Stopwatch() {
  const [time, setTime] = useState(0);
  const intervalRef = useRef(null);

  function handleStart() {
    intervalRef.current = setInterval(() => {
      setTime(t => t + 1);
    }, 10);
  }

  function handleStop() {
    clearInterval(intervalRef.current);
  }

  return (
    <div>
      <p>Time: {(time / 100).toFixed(2)}s</p>
      <button onClick={handleStart}>Start</button>
      <button onClick={handleStop}>Stop</button>
    </div>
  );
}

视频播放器控制(Context7 模式):

import { useRef, useState } from 'react';

function VideoPlayer({ src, isPlaying }) {
  const ref = useRef(null);

  useEffect(() => {
    if (isPlaying) {
      ref.current.play();
    } else {
      ref.current.pause();
    }
  }, [isPlaying]);

  return <video ref={ref} src={src} loop playsInline />;
}

function App() {
  const [isPlaying, setIsPlaying] = useState(false);

  return (
    <>
      <button onClick={() => setIsPlaying(!isPlaying)}>
        {isPlaying ? 'Pause' : 'Play'}
      </button>
      <VideoPlayer
        isPlaying={isPlaying}
        src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
      />
    </>
  );
}

状态管理模式

本地状态模式

对于无需共享的组件专属数据,使用本地状态。

function LoginForm() {
  const [formData, setFormData] = useState({
    username: '',
    password: '',
    rememberMe: false
  });
  const [errors, setErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);

  const handleChange = (field) => (e) => {
    const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
    setFormData(prev => ({ ...prev, [field]: value }));
  };

  const validate = () => {
    const newErrors = {};
    if (!formData.username) newErrors.username = 'Required';
    if (!formData.password) newErrors.password = 'Required';
    return newErrors;
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    const newErrors = validate();

    if (Object.keys(newErrors).length > 0) {
      setErrors(newErrors);
      return;
    }

    setIsSubmitting(true);
    try {
      await login(formData);
    } catch (error) {
      setErrors({ submit: error.message });
    } finally {
      setIsSubmitting(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={formData.username}
        onChange={handleChange('username')}
        placeholder="Username"
      />
      {errors.username && <span>{errors.username}</span>}

      <input
        type="password"
        value={formData.password}
        onChange={handleChange('password')}
        placeholder="Password"
      />
      {errors.password && <span>{errors.password}</span>}

      <label>
        <input
          type="checkbox"
          checked={formData.rememberMe}
          onChange={handleChange('rememberMe')}
        />
        Remember me
      </label>

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Logging in...' : 'Log in'}
      </button>

      {errors.submit && <div>{errors.submit}</div>}
    </form>
  );
}
Context API 模式

对于主题、用户身份验证或偏好设置等全局或广泛共享的状态,使用 Context。

带有 Provider 的主题 Context:

import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext(null);

export 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>
  );
}

export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within ThemeProvider');
  }
  return context;
}

// Usage
function App() {
  return (
    <ThemeProvider>
      <Page />
    </ThemeProvider>
  );
}

function Page() {
  const { theme, toggleTheme } = useTheme();

  return (
    <div className={`page-${theme}`}>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
}
Reducer + Context 模式(Context7 最佳实践)

将 useReducer 与 Context 结合,实现可扩展的状态管理。

使用 Reducer + Context 管理任务:

import { createContext, useContext, useReducer } from 'react';

// Context for tasks data
const TasksContext = createContext(null);

// Context for dispatch function
const TasksDispatchContext = createContext(null);

export function TasksProvider({ children }) {
  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);

  return (
    <TasksContext.Provider value={tasks}>
      <TasksDispatchContext.Provider value={dispatch}>
        {children}
      </TasksDispatchContext.Provider>
    </TasksContext.Provider>
  );
}

export function useTasks() {
  return useContext(TasksContext);
}

export function useTasksDispatch() {
  return useContext(TasksDispatchContext);
}

function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [...tasks, {
        id: action.id,
        text: action.text,
        done: false
      }];
    }
    case 'changed': {
      return tasks.map(t => {
        if (t.id === action.task.id) {
          return action.task;
        }
        return t;
      });
    }
    case 'deleted': {
      return tasks.filter(t => t.id !== action.id);
    }
    default: {
      throw Error('Unknown action: ' + action.type);
    }
  }
}

const initialTasks = [
  { id: 0, text: 'Philosopher's Path', done: true },
  { id: 1, text: 'Visit the temple', done: false },
  { id: 2, text: 'Drink matcha', done: false }
];

// Component using the pattern
function AddTask() {
  const [text, setText] = useState('');
  const dispatch = useTasksDispatch();

  return (
    <>
      <input
        placeholder="Add task"
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <button onClick={() => {
        setText('');
        dispatch({
          type: 'added',
          id: nextId++,
          text: text,
        });
      }}>Add</button>
    </>
  );
}

function TaskList() {
  const tasks = useTasks();
  return (
    <ul>
      {tasks.map(task => (
        <Task key={task.id} task={task} />
      ))}
    </ul>
  );
}

function Task({ task }) {
  const [isEditing, setIsEditing] = useState(false);
  const dispatch = useTasksDispatch();

  let taskContent;
  if (isEditing) {
    taskContent = (
      <>
        <input
          value={task.text}
          onChange={e => {
            dispatch({
              type: 'changed',
              task: {
                ...task,
                text: e.target.value
              }
            });
          }} />
        <button onClick={() => setIsEditing(false)}>
          Save
        </button>
      </>
    );
  } else {
    taskContent = (
      <>
        {task.text}
        <button onClick={() => setIsEditing(true)}>
          Edit
        </button>
      </>
    );
  }

  return (
    <label>
      <input
        type="checkbox"
        checked={task.done}
        onChange={e => {
          dispatch({
            type: 'changed',
            task: {
              ...task,
              done: e.target.checked
            }
          });
        }}
      />
      {taskContent}
      <button onClick={() => {
        dispatch({
          type: 'deleted',
          id: task.id
        });
      }}>
        Delete
      </button>
    </label>
  );
}

let nextId = 3;

自定义 Hooks

自定义 hooks 让你能够将组件逻辑提取到可复用的函数中。

基本自定义 Hook
import { useState, useEffect } from 'react';

function useWindowSize() {
  const [size, setSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight
  });

  useEffect(() => {
    function handleResize() {
      setSize({
        width: window.innerWidth,
        height: window.innerHeight
      });
    }

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return size;
}

// Usage
function Component() {
  const { width, height } = useWindowSize();
  return <div>{width} x {height}</div>;
}
在线状态 Hook(Context7 模式)
import { useState, useEffect } from 'react';

function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(true);

  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }

    function handleOffline() {
      setIsOnline(false);
    }

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return isOnline;
}

// Usage
function StatusBar() {
  const isOnline = useOnlineStatus();
  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}
表单 Hook
function useForm(initialValues, onSubmit) {
  const [values, setValues] = useState(initialValues);
  const [errors, setErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);

  const handleChange = (name, value) => {
    setValues(prev => ({ ...prev, [name]: value }));
    // Clear error when user starts typing
    if (errors[name]) {
      setErrors(prev => ({ ...prev, [name]: null }));
    }
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    setIsSubmitting(true);

    try {
      await onSubmit(values);
    } catch (error) {
      setErrors({ submit: error.message });
    } finally {
      setIsSubmitting(false);
    }
  };

  const reset = () => {
    setValues(initialValues);
    setErrors({});
  };

  return {
    values,
    errors,
    isSubmitting,
    handleChange,
    handleSubmit,
    setErrors,
    reset
  };
}

// Usage
function ContactForm() {
  const { values, errors, isSubmitting, handleChange, handleSubmit } = useForm(
    { name: '', email: '', message: '' },
    async (data) => {
      await fetch('/api/contact', {
        method: 'POST',
        body: JSON.stringify(data)
      });
    }
  );

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={values.name}
        onChange={(e) => handleChange('name', e.target.value)}
      />
      {errors.name && <span>{errors.name}</span>}

      <input
        value={values.email}
        onChange={(e) => handleChange('email', e.target.value)}
      />
      {errors.email && <span>{errors.email}</span>}

      <textarea
        value={values.message}
        onChange={(e) => handleChange('message', e.target.value)}
      />
      {errors.message && <span>{errors.message}</span>}

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Sending...' : 'Send'}
      </button>
    </form>
  );
}
获取数据 Hook
function useFetch(url, options = {}) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let cancelled = false;

    async function fetchData() {
      try {
        setLoading(true);
        const response = await fetch(url, options);

        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }

        const result = await response.json();

        if (!cancelled) {
          setData(result);
          setError(null);
        }
      } catch (err) {
        if (!cancelled) {
          setError(err.message);
          setData(null);
        }
      } finally {
        if (!cancelled) {
          setLoading(false);
        }
      }
    }

    fetchData();

    return () => {
      cancelled = true;
    };
  }, [url, JSON.stringify(options)]);

  return { data, loading, error };
}

// Usage
function UserProfile({ userId }) {
  const { data, loading, error } = useFetch(`/api/users/${userId}`);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return <div>{data.name}</div>;
}
本地存储 Hook
function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  const setValue = (value) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error(error);
    }
  };

  return [storedValue, setValue];
}

// Usage
function Settings() {
  const [theme, setTheme] = useLocalStorage('theme', 'light');
  const [fontSize, setFontSize] = useLocalStorage('fontSize', 16);

  return (
    <div>
      <select value={theme} onChange={(e) => setTheme(e.target.value)}>
        <option value="light">Light</option>
        <option value="dark">Dark</option>
      </select>

      <input
        type="number"
        value={fontSize}
        onChange={(e) => setFontSize(parseInt(e.target.value))}
      />
    </div>
  );
}

性能优化

React.memo

缓存组件,避免不必要的重新渲染。

import { memo } from 'react';

const ExpensiveComponent = memo(function ExpensiveComponent({ data, onAction }) {
  console.log('Rendering expensive component');

  return (
    <div>
      {data.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
      <button onClick={onAction}>Action</button>
    </div>
  );
});

// With custom comparison
const CustomMemoComponent = memo(
  function Component({ user }) {
    return <div>{user.name}</div>;
  },
  (prevProps, nextProps) => {
    // Return true if props are equal (skip re-render)
    return prevProps.user.id === nextProps.user.id;
  }
);
延迟加载

按需加载组件,以减小初始包的大小。

import { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));
const AdminPanel = lazy(() => import('./AdminPanel'));

function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <HeavyComponent />
      </Suspense>
    </div>
  );
}

// Lazy loading with routes
function Dashboard() {
  const [showAdmin, setShowAdmin] = useState(false);

  return (
    <div>
      <button onClick={() => setShowAdmin(true)}>
        Show Admin Panel
      </button>

      {showAdmin && (
        <Suspense fallback={<Spinner />}>
          <AdminPanel />
        </Suspense>
      )}
    </div>
  );
}
代码拆分

将代码拆分为更小的块,以获得更好的性能。

import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>Loading page...</div>}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/dashboard" element={<Dashboard />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}
虚拟滚动

在大型列表中仅渲染可见项。

function VirtualList({ items, height, itemHeight }) {
  const [scrollTop, setScrollTop] = useState(0);

  const startIndex = Math.floor(scrollTop / itemHeight);
  const endIndex = Math.min(
    startIndex + Math.ceil(height / itemHeight),
    items.length
  );

  const visibleItems = items.slice(startIndex, endIndex);

  const offsetY = startIndex * itemHeight;
  const totalHeight = items.length * itemHeight;

  return (
    <div
      style={{ height, overflow: 'auto' }}
      onScroll={(e) => setScrollTop(e.target.scrollTop)}
    >
      <div style={{ height: totalHeight, position: 'relative' }}>
        <div style={{ transform: `translateY(${offsetY}px)` }}>
          {visibleItems.map((item, index) => (
            <div
              key={startIndex + index}
              style={{ height: itemHeight }}
            >
              {item}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

Context7 研究总结的最佳实践

1. 正确设置依赖数组

始终在 useEffect、useMemo 和 useCallback 中包含所有依赖项。

// ❌ Bad - missing dependencies
useEffect(() => {
  fetchData(userId);
}, []);

// ✅ Good - all dependencies included
useEffect(() => {
  fetchData(userId);
}, [userId]);
2. 清理函数

始终清理副作用,以防止内存泄漏。

useEffect(() => {
  const subscription = api.subscribe(id);

  return () => {
    subscription.unsubscribe();
  };
}, [id]);
3. 分离关注点

拆分数据和 dispatch 的 context,以优化重新渲染。

// ✅ Good - separate contexts
const TasksContext = createContext(null);
const TasksDispatchContext = createContext(null);

// Components that only dispatch won't re-render when tasks change
function AddTask() {
  const dispatch = useTasksDispatch(); // No re-render when tasks change
  // ...
}
4. 避免内联创建对象

使用 useMemo 或 useCallback,避免不必要的重新渲染。

// ❌ Bad - new object on every render
<Component style={{ margin: 10 }} />

// ✅ Good - memoized object
const style = useMemo(() => ({ margin: 10 }), []);
<Component style={style} />
5. 状态更新函数

当新状态依赖于之前的状态时,使用更新函数。

// ❌ Bad - may use stale state
setCount(count + 1);

// ✅ Good - uses current state
setCount(prevCount => prevCount + 1);
6. 提取复杂逻辑

将复杂的状态逻辑移至 reducers 或自定义 hooks 中。

// ✅ Good - complex logic in reducer
function cartReducer(state, action) {
  switch (action.type) {
    case 'add_item':
      // Complex logic here
      return newState;
    case 'remove_item':
      // Complex logic here
      return newState;
    default:
      return state;
  }
}
7. 为列表提供 key 属性

始终为列表项提供唯一的 key。

// ❌ Bad - using index as key
{items.map((item, index) => <div key={index}>{item}</div>)}

// ✅ Good - using unique ID
{items.map(item => <div key={item.id}>{item.text}</div>)}
8. 受控组件

表单输入优先使用受控组件。

function Form() {
  const [value, setValue] = useState('');

  return (
    <input
      value={value}
      onChange={(e) => setValue(e.target.value)}
    />
  );
}
9. 错误边界

实现错误边界,以便妥善捕获和处理错误。

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  componentDidCatch(error, errorInfo) {
    console.error('Error caught:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

// Usage
<ErrorBoundary>
  <MyComponent />
</ErrorBoundary>
10. Prop 类型或 TypeScript

使用 PropTypes 或 TypeScript 进行类型检查。

import PropTypes from 'prop-types';

function User({ name, age, email }) {
  return <div>{name} ({age})</div>;
}

User.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number.isRequired,
  email: PropTypes.string
};

User.defaultProps = {
  email: '[email protected]'
};

其他示例

示例 1:多步骤表单
function MultiStepForm() {
  const [step, setStep] = useState(1);
  const [formData, setFormData] = useState({
    personalInfo: {},
    address: {},
    preferences: {}
  });

  const updateFormData = (section, data) => {
    setFormData(prev => ({
      ...prev,
      [section]: { ...prev[section], ...data }
    }));
  };

  const nextStep = () => setStep(s => s + 1);
  const prevStep = () => setStep(s => s - 1);

  return (
    <div>
      {step === 1 && (
        <PersonalInfoStep
          data={formData.personalInfo}
          onNext={(data) => {
            updateFormData('personalInfo', data);
            nextStep();
          }}
        />
      )}
      {step === 2 && (
        <AddressStep
          data={formData.address}
          onNext={(data) => {
            updateFormData('address', data);
            nextStep();
          }}
          onPrev={prevStep}
        />
      )}
      {step === 3 && (
        <PreferencesStep
          data={formData.preferences}
          onSubmit={(data) => {
            updateFormData('preferences', data);
            submitForm({ ...formData, preferences: data });
          }}
          onPrev={prevStep}
        />
      )}
    </div>
  );
}
示例 2:无限滚动
function InfiniteScroll() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);

  const loadMore = useCallback(async () => {
    if (loading || !hasMore) return;

    setLoading(true);
    try {
      const newItems = await fetchItems(page);
      setItems(prev => [...prev, ...newItems]);
      setHasMore(newItems.length > 0);
      setPage(p => p + 1);
    } finally {
      setLoading(false);
    }
  }, [page, loading, hasMore]);

  useEffect(() => {
    const handleScroll = () => {
      if (
        window.innerHeight + window.scrollY >=
        document.documentElement.scrollHeight - 500
      ) {
        loadMore();
      }
    };

    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, [loadMore]);

  return (
    <div>
      {items.map(item => <Item key={item.id} data={item} />)}
      {loading && <div>Loading...</div>}
      {!hasMore && <div>No more items</div>}
    </div>
  );
}
示例 3:防抖搜索
function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
}

function SearchComponent() {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearchTerm = useDebounce(searchTerm, 500);
  const [results, setResults] = useState([]);

  useEffect(() => {
    if (debouncedSearchTerm) {
      searchAPI(debouncedSearchTerm).then(setResults);
    } else {
      setResults([]);
    }
  }, [debouncedSearchTerm]);

  return (
    <div>
      <input
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        placeholder="Search..."
      />
      <ul>
        {results.map(result => (
          <li key={result.id}>{result.title}</li>
        ))}
      </ul>
    </div>
  );
}
示例 4:使用 Portal 的模态框
import { createPortal } from 'react-dom';

function Modal({ isOpen, onClose, children }) {
  if (!isOpen) return null;

  return createPortal(
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal-content" onClick={(e) => e.stopPropagation()}>
        <button className="close-button" onClick={onClose}>×</button>
        {children}
      </div>
    </div>,
    document.body
  );
}

// Usage
function App() {
  const [isModalOpen, setIsModalOpen] = useState(false);

  return (
    <div>
      <button onClick={() => setIsModalOpen(true)}>Open Modal</button>
      <Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
        <h2>Modal Content</h2>
        <p>This is a modal dialog</p>
      </Modal>
    </div>
  );
}
示例 5:拖放
function DragDropList() {
  const [items, setItems] = useState([
    { id: 1, text: 'Item 1' },
    { id: 2, text: 'Item 2' },
    { id: 3, text: 'Item 3' }
  ]);

  const [draggedItem, setDraggedItem] = useState(null);

  const handleDragStart = (item) => {
    setDraggedItem(item);
  };

  const handleDragOver = (e) => {
    e.preventDefault();
  };

  const handleDrop = (targetItem) => {
    if (!draggedItem || draggedItem.id === targetItem.id) return;

    const draggedIndex = items.findIndex(i => i.id === draggedItem.id);
    const targetIndex = items.findIndex(i => i.id === targetItem.id);

    const newItems = [...items];
    newItems.splice(draggedIndex, 1);
    newItems.splice(targetIndex, 0, draggedItem);

    setItems(newItems);
    setDraggedItem(null);
  };

  return (
    <ul>
      {items.map(item => (
        <li
          key={item.id}
          draggable
          onDragStart={() => handleDragStart(item)}
          onDragOver={handleDragOver}
          onDrop={() => handleDrop(item)}
        >
          {item.text}
        </li>
      ))}
    </ul>
  );
}

总结

这项 React 开发技能涵盖:

  1. 核心概念:组件、JSX、Props、State
  2. 基本 Hooks:useState、useEffect、useContext、useReducer、useMemo、useCallback、useRef
  3. 状态管理:本地状态、Context API、Reducer + Context 模式
  4. 自定义 Hooks:可复用逻辑的提取模式
  5. 性能:记忆化、延迟加载、代码拆分、虚拟滚动
  6. 最佳实践:来自 Context7 研究,包括正确设置依赖项、清理副作用和分离关注点
  7. 实际示例:表单、无限滚动、搜索、模态框、拖放

这些模式和示例基于 React 官方文档(信任评分:10),体现了现代 React 开发实践,侧重于函数组件和 Hooks。

评论

支持 Markdown