React 核心概念速查:JSX、Render、状态与事件
本文目录
本文保留历史归档路径和日期,重新校正早期 React 总结中的过时说法。完整学习顺序见 React 知识地图;本页只做概念速查。React、React DOM 和路由器都要按项目 lockfile 中的主版本阅读对应文档。
JSX、元素与 DOM
JSX 是写在 JavaScript 中的语法扩展,需要由构建工具转换。它通常被编译成 React 元素描述,不是浏览器 DOM,也不能把 JSX 本身简单等同于 React 内部的协调算法。经典转换与现代自动 JSX Runtime 的输出形式不同;应用代码应使用公开 API,不依赖编译产物细节。
function Greeting({ name }) {
return <h1 className="greeting">你好,{name}</h1>;
}
JSX 里常见规则:
- 自定义组件名以大写字母开头,内置 HTML 标签名用小写。
- JavaScript 表达式放在大括号中;属性名采用 React DOM 约定,例如 className 和 htmlFor。
- 返回多个相邻节点时可使用容器或 Fragment;列表里的 Fragment 需要 key 时使用显式 Fragment 组件。
- 事件处理器传函数引用,例如 onClick={handleSave},不要在 render 时直接调用 handleSave()。
Render 与 Commit
组件 render 是根据当前 props、state 和 context 计算 UI 描述。React 接着协调新旧树,并在 commit 阶段应用必要的 DOM 变化。render 不等于“每次都重建整个页面”;render 也可能重复、暂停或被放弃,因此组件 render 必须保持纯粹,不能在里面发请求、注册监听、直接修改 DOM 或改全局变量。
React 的协调会参考元素类型、组件位置和 key 来判断是否保留组件身份与状态。把内部 Diff 算法简化成“只做同层对比、发现新元素就替换”容易误导;工程代码应遵守公开的组件身份与 key 规则,而不是依赖内部实现细节。
函数组件、类组件与状态
函数组件不是“无状态组件”。现代函数组件可以用 Hooks 管理 state、context、引用以及与外部系统的同步;新代码通常优先使用函数组件。类组件仍受支持,老项目中会遇到 state、setState 和生命周期方法,不必因此立刻做全量迁移,也不要把类组件想象成必然更慢。
Props 是父组件提供给子组件的只读输入;子组件要反馈变化时,应调用父组件传下来的回调。State 属于拥有它的组件,一次 render 中读取到的是该次渲染的状态快照。若下一状态依赖上一状态,应使用 updater:
setCount((current) => current + 1);
不要直接修改 state 对象或数组。把状态放在需要它的最近共同父组件;过滤列表、合计和标签通常可由现有 props/state 直接计算,不要无故再复制一份 state。更完整说明见 组件和 state 与 条件、列表和表单。
列表与 key
key 帮助 React 在同一组兄弟元素之间识别数据项。key 只需在同级兄弟项中唯一,不要求整个应用全局唯一;它不会自动成为组件 props。优先使用数据里的稳定 ID,不要用随机数或当前时间。数组索引只适用于不会插入、删除或重排的静态列表。
function TaskList({ tasks }) {
return (
<ul>
{tasks.map((task) => (
<li key={task.id}>{task.title}</li>
))}
</ul>
);
}
事件、表单与条件渲染
React DOM 事件对象提供 target、currentTarget、preventDefault() 和 stopPropagation() 等接口。target 是实际触发节点,currentTarget 是当前处理器所在节点。React DOM 中 persist() 不再需要,也不会让事件对象“保留更久”;异步逻辑需要值时,直接先保存所需字段。React Native 与旧 React DOM 版本应按各自版本文档处理。
表单提交用 onSubmit;需要阻止浏览器默认导航时调用 preventDefault()。form 内的普通操作按钮写 type="button",提交按钮写 type="submit"。受控输入用 state 提供 value 或 checked,并在 onChange 里同步更新。条件渲染中注意 0 && <Panel /> 会把数字 0 显示出来;需要布尔判断时显式写 count > 0。
根节点、Fragment、Portal 与代码分割
浏览器客户端新根节点使用 react-dom/client 的 createRoot;服务端已生成 HTML 时要使用 hydrateRoot,而不是清空并重建。React 框架通常替应用管理根节点。渲染一个独立弹层不应另开 React root,优先考虑 react-dom 的 createPortal。Fragment 可以组合多个节点且不增加 DOM 包装层。
React.lazy 配合 Suspense 可延迟加载不需要首屏立即展示的组件;lazy 声明放模块顶层,并为失败准备错误边界。Suspense 不是任意 useEffect 数据请求的自动加载状态管理器。
样式与脚手架边界
React DOM 使用 className 不代表样式必须全局。普通 CSS、CSS Modules、CSS-in-JS 的作用域与构建配置由项目工具决定;CSS Modules 不是 React 内置功能,styled-components 等也需要额外依赖。检查仓库的 Vite、Webpack、框架配置和样式依赖,再采用对应写法。
旧版 Create React App 项目里的 proxy 通常只影响本地开发服务器,不会自动配置生产反向代理、CORS、认证或 API 地址。部署路由回退、环境变量和 API 跨域策略都要在实际生产架构中单独配置。
旧 API 到现代方向
| 历史写法 | 现代方向 |
|---|---|
| ReactDOM.render | 客户端新根使用 createRoot;服务端 HTML 使用 hydrateRoot |
| ReactDOM.createProtal(拼写也常错) | 从 react-dom 导入 createPortal |
| 无状态函数组件 | 函数组件配合 Hooks;是否有状态不由函数/类语法决定 |
| event.persist() | React DOM 不需要;React Native 按对应版本判断 |
| 直接修改 state 后 forceUpdate() | 通过 setState 或 Hook setter 创建状态更新 |
| 随机数或数组位置作动态列表 key | 使用稳定业务 ID;只有静态列表才考虑索引 |
继续阅读
- 组件与 state
- React 条件与列表渲染、组件复用和表单状态
- React 生命周期与 Effect 清理
- React 组件通信、Context、懒加载与 Portal
- React Hooks:状态、Effect 与性能边界
- React Router:嵌套路由、数据加载与版本迁移
- React 面试题
官方资料
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。