React 19 入门教程
组件设计原则
本教程共 50 篇 · 第 48 篇 · 更新于 2026-07-29 · 约 8 分钟阅读
React组件设计设计原则组合单一职责最佳实践
48. 组件设计原则
本节目标:掌握组件拆分的基本原则,写出可复用、可维护的 React 组件。
单一职责原则
一个组件只做一件事。它干得好好的,你一看名字就知道它干啥。
反例:
// ❌ 一个组件干了四件事:请求、列表、搜索、分页
function UserPage() {
const [users, setUsers] = useState([]);
const [search, setSearch] = useState('');
const [page, setPage] = useState(1);
useEffect(() => { /* 请求数据 */ }, [search, page]);
return (
<div>
<input value={search} onChange={...} />
<ul>{users.map(...)}</ul>
<button onClick={() => setPage(page + 1)}>下一页</button>
</div>
);
}
拆分后:
// ✅ 各司其职
function UserPage() {
return (
<div>
<SearchInput />
<UserList />
<Pagination />
</div>
);
}
Tip判断标准:如果组件的注释里要写”和”字,说明职责不单一。
组合优于继承
React 不推荐用继承来复用代码。用组合(components as props)。
// ❌ 继承模式——React 不支持也不推荐
class BaseModal extends React.Component {
render() { return <div className="modal">{this.renderContent()}</div>; }
}
class ConfirmModal extends BaseModal {
renderContent() { return <button>确认</button>; }
}
// ✅ 组合模式——用 children
function Modal({ title, children, onClose }) {
return (
<div className="modal">
<h2>{title}</h2>
{children}
<button onClick={onClose}>关闭</button>
</div>
);
}
function ConfirmDialog() {
return (
<Modal title="确认删除?">
<p>此操作不可撤销</p>
<button>确认删除</button>
</Modal>
);
}
children prop 是最基础的组合手段。更复杂的场景用 render prop 或插槽模式。
单向数据流
React 的数据流是单向的:从父到子,通过 props。
function Parent() {
const [name, setName] = useState('Alice');
return (
<>
<DisplayUser name={name} />
<EditUser name={name} onNameChange={setName} />
</>
);
}
function DisplayUser({ name }) {
return <p>当前用户:{name}</p>;
}
function EditUser({ name, onNameChange }) {
return <input value={name} onChange={e => onNameChange(e.target.value)} />;
}
子组件不直接改父组件的 state。通过回调函数”通知”父组件去改。
Note单向数据流让数据流向清晰。你永远知道 state 在哪里、谁改了它。
状态提升
当多个组件需要共享同一个 state,把它移到最近的共同祖先。
// ❌ 每个组件各管各的
function TemperatureInput() {
const [celsius, setCelsius] = useState(0);
return <input value={celsius} onChange={...} />;
}
function FahrenheitInput() {
const [fahrenheit, setFahrenheit] = useState(32);
return <input value={fahrenheit} onChange={...} />;
}
// ✅ 状态提升到共同父组件
function TemperatureConverter() {
const [celsius, setCelsius] = useState(0);
const fahrenheit = celsius * 9 / 5 + 32;
return (
<>
<input value={celsius} onChange={e => setCelsius(e.target.value)} />
<input value={fahrenheit} readOnly />
</>
);
}
什么时候不提升:如果 state 只在一个组件内部用,就不用提。
受控 vs 非受控
表单组件有两种写法。
受控组件:value 由 React state 管理。
function ControlledInput() {
const [value, setValue] = useState('');
return (
<input
value={value}
onChange={e => setValue(e.target.value)}
/>
);
}
优点:输入值完全由 React 控制,校验、格式化都方便。
缺点:每次按键都触发重渲染。
非受控组件:value 由 DOM 自己管理。
import { useRef } from 'react';
function UncontrolledInput() {
const inputRef = useRef(null);
function handleSubmit() {
console.log(inputRef.current.value);
}
return (
<>
<input ref={inputRef} defaultValue="" />
<button onClick={handleSubmit}>提交</button>
</>
);
}
优点:性能好(无重渲染)。
缺点:不能实时校验、不能条件禁用提交按钮。
Tip大多数场景用受控组件。只在文件上传、大量输入框(>100)时考虑非受控。
组件拆分粒度
组件不是越小越好,也不是越大越好。
信号——该拆了:
- 组件超过 200 行
- 有重复的 JSX 片段
- 注释里出现”和”字
- 一个 prop 影响了组件的两种不同行为
信号——不该拆:
- 拆出来只有一个地方用
- 拆出来没有独立语义
- 拆出来只是为了减少行数
// 折中:内部提取 render 函数
function SearchResults({ results, isLoading }) {
if (isLoading) return <Spinner />;
return (
<ul>
{results.map(item => (
<li key={item.id}>
<h3>{item.title}</h3>
<p>{item.description}</p>
</li>
))}
</ul>
);
}
// 等 item 变复杂了再拆
function SearchResultItem({ item }) {
return (
<li>
<h3>{item.title}</h3>
<p>{item.description}</p>
</li>
);
}
props 设计原则
props 要保持扁平
// ❌ 嵌套对象
<UserCard user={{ name: 'Alice', age: 25, address: { city: 'Beijing' } }} />
// ✅ 扁平 props
<UserCard name="Alice" age={25} city="Beijing" />
布尔 prop 用 is/has 前缀
<Button isLoading disabled />
<Modal hasFooter isClosable />
回调 prop 用 on + 动词
<Form onSubmit={handleSubmit} />
<Button onClick={handleClick} />
<Input onChangeText={handleChange} />
自定义 Hook 抽逻辑
组件里出现重复的”状态 + effect”模式,抽成自定义 Hook。
// ❌ 每个组件都写一遍
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/users').then(r => r.json()).then(data => {
setUsers(data);
setLoading(false);
});
}, []);
// ...
}
// ✅ 抽成 Hook
function useUsers() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/users').then(r => r.json()).then(data => {
setUsers(data);
setLoading(false);
});
}, []);
return { users, loading };
}
function UserList() {
const { users, loading } = useUsers();
// ...
}
Note自定义 Hook 是 React 逻辑复用的主要手段。组件是 UI 复用的手段。两者配合使用。
小结
- 一个组件只做一件事
- 用组合(children/render props)代替继承
- 数据单向流动,state 变化通过回调上传
- 共享状态提升到最近共同祖先
- 表单优先用受控组件
- props 扁平、命名规范
- 重复逻辑抽成自定义 Hook