react-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Development

React开发

Modern React patterns from the official docs and community expertise.
来自官方文档和社区经验的现代React模式。

Instructions

操作指南

When working with React code, apply these principles in order of importance:
在处理React代码时,请按照以下优先级应用这些原则:

1. Think in React

1. 以React思维思考

Follow the 5-step process for building React UIs:
  1. Break UI into component hierarchy — One component = one responsibility
  2. Build static version first — Use props only, no state yet
  3. Find minimal state — Ask: Does it change? Is it passed via props? Can it be computed?
  4. Identify where state lives — Find closest common parent of components that need it
  5. Add inverse data flow — Pass callbacks down to update parent state
遵循构建React UI的5步流程:
  1. 将UI拆分为组件层级 — 一个组件对应一项职责
  2. 先构建静态版本 — 仅使用props,暂不使用state
  3. 找出最小化状态 — 自问:它会变化吗?是通过props传递的吗?能否通过计算得到?
  4. 确定状态的存放位置 — 找到需要该状态的组件的最近共同父组件
  5. 添加反向数据流 — 向下传递回调函数以更新父组件状态

2. Keep Components Pure

2. 保持组件纯净

Components must be pure functions during rendering:
jsx
// ❌ Mutates external variable
let guest = 0;
function Cup() {
  guest = guest + 1;
  return <h2>Guest #{guest}</h2>;
}

// ✅ Uses props
function Cup({ guest }) {
  return <h2>Guest #{guest}</h2>;
}
Rules:
  • Never mutate props, state, or context during render
  • Same inputs = same output
  • Side effects belong in event handlers or useEffect
组件在渲染期间必须是纯函数:
jsx
// ❌ 修改外部变量
let guest = 0;
function Cup() {
  guest = guest + 1;
  return <h2>Guest #{guest}</h2>;
}

// ✅ 使用props
function Cup({ guest }) {
  return <h2>Guest #{guest}</h2>;
}
规则:
  • 渲染期间绝不要修改props、state或context
  • 相同输入必须得到相同输出
  • 副作用应放在事件处理器或useEffect中

3. State Management

3. 状态管理

Minimize state:
jsx
// ❌ Redundant state
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState(''); // redundant!

// ✅ Compute during render
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const fullName = firstName + ' ' + lastName;
Lift state up when siblings need the same data — move to closest common parent.
Use key to reset state:
jsx
// Forces fresh component instance when recipient changes
<Chat key={recipientId} contact={recipient} />
最小化状态:
jsx
// ❌ 冗余状态
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState(''); // 冗余!

// ✅ 在渲染时计算
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const fullName = firstName + ' ' + lastName;
当兄弟组件需要相同数据时提升状态 — 将状态移至最近的共同父组件。
使用key重置状态:
jsx
// 当recipient变化时强制创建新的组件实例
<Chat key={recipientId} contact={recipient} />

4. Hooks Rules

4. Hooks规则

Only call hooks at the top level:
  • Never in loops, conditions, or nested functions
  • Never after early returns
  • React relies on consistent call order
Only call hooks from React functions:
  • Function components or custom hooks only
仅在顶层调用Hooks:
  • 绝不要在循环、条件判断或嵌套函数中调用
  • 绝不要在提前return之后调用
  • React依赖于稳定的调用顺序
仅在React函数中调用Hooks:
  • 只能在函数组件或自定义Hooks中调用

5. Effects — Use Sparingly

5. Effects — 谨慎使用

Effects are for synchronizing with external systems, not for state logic.
You DON'T need useEffect for:
ScenarioInstead of EffectDo This
Transform data for render
useEffect
+
setState
Calculate during render
Cache expensive calculations
useEffect
+
setState
useMemo
Reset state on prop change
useEffect
+
setState
Use
key
prop
Handle user events
useEffect
watching state
Event handler directly
Notify parent of changes
useEffect
calling callback
Call in event handler
When you DO need useEffect:
  • Fetching data (with cleanup for race conditions)
  • Subscribing to events (with cleanup to unsubscribe)
  • Controlling non-React widgets
  • Connecting to external systems
Always add cleanup:
jsx
useEffect(() => {
  const connection = createConnection();
  connection.connect();
  return () => connection.disconnect(); // cleanup
}, []);
Effects用于与外部系统同步,而非处理状态逻辑。
不需要使用useEffect的场景:
场景替代useEffect的方案具体操作
转换渲染用的数据
useEffect
+
setState
在渲染时计算
缓存昂贵的计算结果
useEffect
+
setState
使用
useMemo
当props变化时重置状态
useEffect
+
setState
使用
key
属性
处理用户事件监听state的useEffect直接使用事件处理器
通知父组件状态变化调用回调的useEffect在事件处理器中调用
需要使用useEffect的场景:
  • 获取数据(需清理以避免竞态条件)
  • 订阅事件(需清理以取消订阅)
  • 控制非React组件
  • 连接外部系统
始终添加清理逻辑:
jsx
useEffect(() => {
  const connection = createConnection();
  connection.connect();
  return () => connection.disconnect(); // 清理逻辑
}, []);

6. Performance

6. 性能优化

Before reaching for memo:
  1. Move state down — Isolate state to components that use it
  2. Lift content up — Pass expensive subtrees as
    children
Virtualize large lists (50+ items):
jsx
import { FixedSizeList } from 'react-window';

<FixedSizeList height={400} itemCount={1000} itemSize={35}>
  {({ index, style }) => <div style={style}>{items[index]}</div>}
</FixedSizeList>
Code split routes:
jsx
const Dashboard = lazy(() => import('./Dashboard'));

<Suspense fallback={<Loading />}>
  <Dashboard />
</Suspense>
在使用memo之前,请先尝试:
  1. 将状态下移 — 将状态隔离到需要它的组件中
  2. 将内容上移 — 将开销较大的子树作为
    children
    传递
虚拟化大型列表(50项以上):
jsx
import { FixedSizeList } from 'react-window';

<FixedSizeList height={400} itemCount={1000} itemSize={35}>
  {({ index, style }) => <div style={style}>{items[index]}</div>}
</FixedSizeList>
路由代码分割:
jsx
const Dashboard = lazy(() => import('./Dashboard'));

<Suspense fallback={<Loading />}>
  <Dashboard />
</Suspense>

7. Component Design Principles

7. 组件设计原则

From "Writing Resilient Components":
  1. Don't stop the data flow — Read props directly, don't copy to state
  2. Always be ready to render — Don't assume render timing or frequency
  3. No component is a singleton — Design as if rendered multiple times
  4. Keep local state isolated — Only local if it wouldn't sync across copies
来自《编写高韧性组件》:
  1. 不要中断数据流 — 直接读取props,不要复制到state中
  2. 随时准备好渲染 — 不要假设渲染的时机或频率
  3. 没有组件是单例 — 按照可多次渲染的方式设计
  4. 保持局部状态隔离 — 仅当状态无需在多个副本间同步时才作为局部状态

8. Context — Use Judiciously

8. Context — 审慎使用

Good for: theming, current user, routing, widely-needed state.
Try these first:
  1. Pass props explicitly — clearer data flow
  2. Extract components and use
    children
    — avoids prop drilling
jsx
// ❌ Prop drilling
<Layout posts={posts} />

// ✅ Composition
<Layout>
  <Posts posts={posts} />
</Layout>
适用场景:主题设置、当前用户、路由、广泛需要的状态。
请先尝试以下方案:
  1. 显式传递props — 数据流更清晰
  2. 提取组件并使用
    children
    — 避免props透传
jsx
// ❌ Props透传
<Layout posts={posts} />

// ✅ 组合方式
<Layout>
  <Posts posts={posts} />
</Layout>

Common Anti-Patterns

常见反模式

Anti-PatternProblemFix
Copying props to stateStale dataRead props directly
Effect chainsCascading rendersCompute in event handlers
Suppressing effect with refsHides bugsAdd proper cleanup
Derived state in useStateSync issuesCompute during render
Missing keys in listsBroken updatesUse stable unique IDs
Index as key (for reorderable lists)State mismatchUse item IDs
反模式问题修复方案
将props复制到state中数据过期直接读取props
Effect链式调用级联渲染在事件处理器中计算
使用refs抑制Effect隐藏bug添加正确的清理逻辑
在useState中使用派生状态同步问题在渲染时计算
列表中缺少key更新异常使用稳定的唯一ID
(可重排序列表中)使用索引作为key状态不匹配使用项的ID

Examples

示例

Well-Structured Component

结构良好的组件

jsx
function ProductList({ products, onSelect }) {
  // Derived state — computed, not stored
  const sortedProducts = useMemo(
    () => [...products].sort((a, b) => a.name.localeCompare(b.name)),
    [products]
  );

  // Event handler — not useEffect
  function handleClick(product) {
    onSelect(product);
  }

  return (
    <ul>
      {sortedProducts.map(product => (
        <li key={product.id} onClick={() => handleClick(product)}>
          {product.name}
        </li>
      ))}
    </ul>
  );
}
jsx
function ProductList({ products, onSelect }) {
  // 派生状态 — 计算得到,而非存储
  const sortedProducts = useMemo(
    () => [...products].sort((a, b) => a.name.localeCompare(b.name)),
    [products]
  );

  // 事件处理器 — 而非useEffect
  function handleClick(product) {
    onSelect(product);
  }

  return (
    <ul>
      {sortedProducts.map(product => (
        <li key={product.id} onClick={() => handleClick(product)}>
          {product.name}
        </li>
      ))}
    </ul>
  );
}

Data Fetching with Cleanup

带清理逻辑的数据获取

jsx
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

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

    async function fetchUser() {
      const data = await getUser(userId);
      if (!cancelled) setUser(data);
    }

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

  if (!user) return <Loading />;
  return <Profile user={user} />;
}
For detailed performance patterns, see PERFORMANCE.md.
jsx
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

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

    async function fetchUser() {
      const data = await getUser(userId);
      if (!cancelled) setUser(data);
    }

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

  if (!user) return <Loading />;
  return <Profile user={user} />;
}
如需了解详细的性能优化模式,请查看PERFORMANCE.md