React中文版文档有一个明显特点:它并不是把 API 逐条翻译过来,而是按照“描述UI、添加交互、状态管理、逃生舱”的学习路径重新组织了内容。对于已经会用 React 但偶尔要查细节的人来说,直接跳到“API参考”通常更快;对于刚接触框架的人,按顺序读反而能少踩很多坑。本文把这份文档里最常用、也最容易卡住的知识点拆开,结合实际代码说明怎么用、为什么这样设计,以及遇到问题时怎么定位。

一、文档结构怎么读,才不会越看越乱
React中文版文档主要分成“学习React”和“API参考”两大块。学习部分按照思维模型递进,先讲组件和 JSX,再讲 props 和 state,之后才进入 useEffect、useRef 这些偏底层的 Hook。API参考则更像一本手册,适合已经知道名字反查参数和返回值。中文版页面通常支持切换版本,如果本地项目用的是 React 18,建议把文档也切到对应版本,否则并发渲染相关特性的描述容易对不上。
第一次上手时不要从 API 参考开始逐个背。更好的路径是先读“描述UI”和“添加交互”两节,把组件和 state 的代码跑通;遇到数据请求、订阅、DOM 操作等需求时,再翻“逃生舱”里的 useEffect 和 useRef。这样不会一开始就把 useLayoutEffect、useInsertionEffect 这些名字记混,也能更快理解文档为什么要按这个顺序编排。
下面是一个最小组件示例,它展示了文档中最基础的组件和 props 规则。
function Greeting({ name }) {
return <h1>你好,{name}</h1>;
}
export default function App() {
return <Greeting name="React" />;
}
这里需要特别注意,组件名必须首字母大写,否则 JSX 会把它当成普通的 HTML 标签。所以自定义组件要用 <Greeting /> 这种形式。这个规则在文档的“JSX基础”章节里被反复强调,但它仍然是很多人在初学时最容易忽略的语法问题。
二、核心概念:组件、状态与单向数据流
React中文版文档把“UI是数据的函数”这个概念放在很前面。组件接收 props,返回 JSX;state 改变触发重新渲染。这个模型看起来简单,但它限定了数据只能从父组件流向子组件。因此不要在子组件里直接修改 props,也不要在渲染期间修改 state。文档中用大篇幅解释这一点,是因为一旦破坏单向数据流,界面更新会变得很难追踪。
表单是理解单向数据流的好例子。文档中把 value 和 onChange 同时受 React 控制的输入框称为受控组件。受控组件的好处是 React 成为唯一数据源,输入框的值不会脱离 state 自行变化。下面是一个简单的受控输入框:
import { useState } from 'react';
function NameForm() {
const [text, setText] = useState('');
function handleChange(e) {
setText(e.target.value);
}
return (
<input
value={text}
onChange={handleChange}
placeholder="输入名字"
/>
);
}
如果表单字段很多,每个输入框都写一遍 value 和 onChange 会显得重复。这时可以按文档“表单”章节的建议,把多个字段合并成一个对象,再通过一个通用的 handleChange 更新对应字段。核心原则仍然是:输入框显示的值永远来自 state,用户输入只是触发一次 setState。
列表渲染也需要特别留意 key。key 不是用来传给组件的数据,而是帮助 React 识别同一列表中元素身份。key 应当稳定且唯一,通常使用后端返回的 id。除非列表不会重排、不会增删,否则不要直接用数组索引当 key。下面这个示例使用了稳定 id:
const todos = [
{ id: 'a1', text: '阅读文档' },
{ id: 'a2', text: '写示例' },
];
function TodoList() {
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
如果用索引作 key,当列表头部插入一项时,React 会错误地复用旧元素,轻则输入框内容错位,重则组件状态串位。中文版文档在“渲染列表”一节提供了准确示例,建议在开发时直接用这里的规则检查已有代码。
三、高频问题:状态更新、副作用与闭包
经常有人问:为什么在调用 setCount 后立刻读取 count,打印出来的还是旧值?React中文版文档在“状态更新”部分解释得很清楚:更新请求会被放入队列,下一次渲染时才会得到新的 state。同一事件处理函数中的多次 setState 会被合并处理,所以如果连续写两个 setCount(count + 1),最终 count 只会增加 1。
下面先看这个容易出错的写法:
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
setCount(count + 1); // 两次更新仍只增加1
}
return <button onClick={handleClick}>{count}</button>;
}
要基于前一次结果连续更新,应当使用函数式更新:
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(prev => prev + 1);
setCount(prev => prev + 1); // 基于前一次结果,最终加2
}
return <button onClick={handleClick}>{count}</button>;
}
另一个高频问题是 useEffect 的依赖数组。如果在 effect 里读取了某个 state 或 props,却没有把它写进依赖数组,就会产生闭包过期,导致 effect 里一直使用旧值。反过来,如果把对象或数组字面量直接写进依赖数组,每次渲染都会产生新引用,effect 会反复执行。下面这个示例把 name 作为依赖,只在 name 变化时更新页面标题:
import { useState, useEffect } from 'react';
function Title() {
const [name, setName] = useState('');
useEffect(() => {
document.title = `你好,${name}`;
}, [name]);
return (
<input value={name} onChange={e => setName(e.target.value)} />
);
}
文档还专门提醒,很多逻辑其实不需要 effect。比如从现有 state 派生一个值,直接计算往往比写 effect 更简单。遇到“effect 里设置 state 触发重新渲染,又导致 effect 再次执行”的情况,先想一想是否真的需要同步到外部系统,而不是急于增加依赖项。
四、性能优化与严格模式的常见误解
React中文版文档没有把性能优化放在入门阶段,而是放在比较靠后的位置,这是有原因的。memo、useMemo、useCallback 这些 API 只有在出现实际性能问题时才值得引入。React.memo 可以用来跳过组件的无意义重渲染,但前提是传入的 props 是基本类型或引用稳定。否则比较本身带来的成本可能比重新渲染还高。
下面是一个使用 React.memo 的简单示例:
import { memo } from 'react';
const List = memo(function List({ items }) {
return (
<ul>
{items.map(item => (
<li key={item}>{item}</li>
))}
</ul>
);
});
只有父组件更新但 items 的引用没有变化时,List 才会跳过重新渲染。实际开发中,更容易被忽略的是数组或对象在父组件每次渲染时都被重新创建。这时即使用了 React.memo,子组件依然会更新。解决方案通常是把这类数据用 useMemo 包起来,或者把创建逻辑移到组件外部。
最后要提一下严格模式。很多人在开发环境里看到组件函数执行两次、useEffect 挂载执行两次,第一反应是代码出了问题。React中文版文档在“StrictMode”部分说明,这是开发模式下的故意行为,用来帮助发现副作用不符合预期的情况。生产环境中不会出现双调用,因此不必为了消除这个现象而修改代码。正确的做法是确保 effect 具备必要的清理函数,避免重复订阅、重复请求或没有释放的定时器。
React中文版文档React核心概念React常见问题修改时间:2026-09-30 09:16:32