
最近在code review时发现一个有意思的现象:同样是写React,有的同事代码被夸"优雅、可维护",有的却被吐槽"能跑但看着难受"。差距在哪?不是技术栈版本,而是设计模式。
2025年的React早已不是"学会useState和useEffect就能混"的时代了。服务端组件(RSC)、流式渲染(Streaming)、AI驱动UI...新特性层出不穷,但真正拉开差距的,是你用什么思维模式去组织代码。
今天就来硬核拆解10个2025年必须掌握的React模式,不讲虚的,每个都配实战场景和代码示例。看完你就知道,为什么有人写的React代码能用5年不过时。
现状: Class组件在2019年Hooks发布后就是"遗留系统",但2025年还有不少项目在用componentDidMount。
为什么要改? 不是为了赶时髦,而是Hooks让逻辑复用变得无痛。你见过为了复用一个loading状态,写一个HOC包三层的屎山代码吗?
假设你在做一个在线协作工具(类似腾讯文档),需要实时监测用户网络状态:
// hooks/useOnlineStatus.js
import { useState, useEffect } from"react";
exportfunction useOnlineStatus() {
const [online, setOnline] = useState(navigator.onLine);
useEffect(() => {
const handleStatusChange = () => setOnline(navigator.onLine);
window.addEventListener("online", handleStatusChange);
window.addEventListener("offline", handleStatusChange);
return() => {
window.removeEventListener("online", handleStatusChange);
window.removeEventListener("offline", handleStatusChange);
};
}, []);
return online;
}
使用时只需一行:
function DocumentEditor() {
const isOnline = useOnlineStatus();
return (
<div>
{!isOnline && <Banner>网络已断开,修改将保存到本地</Banner>}
<Editor />
</div>
);
}
核心优势:
常见误区: 很多人一上来就Redux/Zustand全局状态,结果一个搜索框的输入都要dispatch action。
正确姿势: 状态放在最小使用范围内,只有真正需要跨组件共享的才提升。
❌ 过度设计(很多国内项目的通病):
// 不必要的全局状态
const searchStore = create((set) => ({
query: '',
setQuery: (q) =>set({ query: q })
}));
function SearchBox() {
const { query, setQuery } = useSearchStore();
return<input value={query} onChange={e => setQuery(e.target.value)} />;
}
✅ 合理设计:
function SearchBox() {
const [query, setQuery] = useState("");
return (
<input
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="搜索商品..."
/>
);
}
为什么这样做?
真实场景: 电商平台的筛选组件,每个筛选条件(价格、品牌、评分)都是独立状态,只有最终的"已选筛选项"才需要提升到父组件。
什么是复合组件? 让多个组件协同工作,但保持使用时的声明式和灵活性。
国内很多UI库(Ant Design、Element)都用这个模式,我们来手写一个简化版:
function Tabs({ children }) {
const [activeIndex, setActiveIndex] = useState(0);
return (
<div className="tabs-container">
{React.Children.map(children, (child, index) =>
React.cloneElement(child, {
isActive: index === activeIndex,
onClick: () => setActiveIndex(index)
})
)}
</div>
);
}
function TabList({ children }) {
return<div className="tab-list">{children}</div>;
}
function Tab({ isActive, onClick, children }) {
return (
<button
className={isActive ? "tab-active" : "tab"}
onClick={onClick}
>
{children}
</button>
);
}
// 组装API
Tabs.List = TabList;
Tabs.Tab = Tab;
使用效果:
<Tabs>
<Tabs.List>
<Tabs.Tab>基本信息</Tabs.Tab>
<Tabs.Tab>商品详情</Tabs.Tab>
<Tabs.Tab>售后保障</Tabs.Tab>
</Tabs.List>
</Tabs>
为什么大厂都这么写?
传统问题: 组件渲染后才在useEffect里fetch,导致"组件显示→loading→数据到达→再渲染"的瀑布流。
2025新范式: 在渲染之前就启动数据请求,配合Suspense。
❌ 老式写法(useEffect Hell):
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUser(userId).then(data => {
setUser(data);
setLoading(false);
});
}, [userId]);
if (loading) return<Spinner />;
return<h1>{user.name}</h1>;
}
✅ 2025标准写法(Suspense + Resource):
// 在组件外部就发起请求
const userResource = fetchUser(userId);
function UserProfile() {
const user = userResource.read(); // 数据未就绪会自动suspend
return <h1>{user.name}</h1>;
}
// 父组件
<Suspense fallback={<Spinner />}>
<UserProfile />
</Suspense>
性能提升原理:
真实案例: 某电商平台首页,因为一个商品卡片组件报错,整个页面白屏。用户投诉暴增,损失千万GMV。
解决方案: 每个独立功能模块都包裹Error Boundary + Suspense。
function SafeProductCard({ productId }) {
return (
<ErrorBoundary fallback={<ProductCardSkeleton />}>
<Suspense fallback={<ProductCardSkeleton />}>
<ProductCard id={productId} />
</Suspense>
</ErrorBoundary>
);
}
为什么必须这么做?
背景: React Server Components在Next.js 13+已经是生产级特性,互联网大厂的部分新项目都在用。
核心思想: 能在服务端跑的就别放客户端,减少JS Bundle体积。
// app/products/page.js (服务端组件,默认)
exportdefaultasyncfunction ProductsPage() {
// 直接在服务端查数据库,不走API
const products = await db.products.findMany();
return<ProductList products={products} />;
}
// components/ProductList.js (客户端组件)
"use client"; // 必须声明
import { useState } from"react";
function ProductList({ products }) {
const [sortBy, setSortBy] = useState("price");
// 需要交互的部分放客户端
return (
<div>
<select onChange={e => setSortBy(e.target.value)}>
<option value="price">价格排序</option>
<option value="sales">销量排序</option>
</select>
{products.sort(sortBy).map(p => <ProductCard key={p.id} {...p} />)}
</div>
);
}
性能对比:
注意: 国内CDN和网络环境下,RSC的优势更明显,因为减少了客户端请求次数。
场景: 一个有20个输入框的复杂表单,每次输入都卡顿。
原因分析:
❌ 全部受控(每次输入都setState):
function ComplexForm() {
const [formData, setFormData] = useState({ /* 20个字段 */ });
// 每次输入都触发整个表单re-render,卡爆
return (
<>
<input
value={formData.name}
onChange={e => setFormData({...formData, name: e.target.value})}
/>
{/* ...还有19个input */}
</>
);
}
✅ 混合方案(关键字段受控,其他用ref):
function ComplexForm() {
const formRef = useRef();
const [realTimeValidField, setRealTimeValidField] = useState("");
const handleSubmit = () => {
const formData = new FormData(formRef.current);
// 提交时才获取所有值
};
return (
<form ref={formRef}>
{/* 需要实时验证的用受控 */}
<input
name="phone"
value={realTimeValidField}
onChange={e => setRealTimeValidField(e.target.value)}
/>
{/* 其他用非受控 */}
<input name="address" defaultValue="" />
<button onClick={handleSubmit}>提交</button>
</form>
);
}
选择原则:
痛点: 用Context做全局状态,一个字段变化,所有消费Context的组件都re-render。
解决方案: 引入selector机制(借鉴Zustand思路)。
// store/userStore.js
const UserContext = createContext();
exportfunction UserProvider({ children }) {
const [user, setUser] = useState({
name: "张三",
avatar: "/avatar.jpg",
vipLevel: 5
});
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
// 带selector的hook
exportfunction useUser(selector) {
const { user } = useContext(UserContext);
return selector ? selector(user) : user;
}
使用时精准订阅:
function UserName() {
// 只订阅name,avatar变化不会导致re-render
const name = useUser(state => state.name);
return <span>{name}</span>;
}
性能提升: 在大型应用(如钉钉、企业微信这类复杂工作台)中,能减少50%+无效渲染。
趋势: 2025年国内大厂(字节Semi Design、阿里Ant Design 5.x)都在推Headless UI。
核心理念: 组件只提供逻辑和无障碍支持,样式完全自定义。
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
function MyDropdown() {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger className="your-custom-button">
打开菜单
</DropdownMenu.Trigger>
<DropdownMenu.Content className="your-custom-menu">
<DropdownMenu.Item className="menu-item">个人中心</DropdownMenu.Item>
<DropdownMenu.Item className="menu-item">退出登录</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
);
}
为什么大厂都在用?
现状: 智谱AI、通义千问等国内大模型已经有成熟的流式API,结合React做AI应用成为新趋势。
function useAICompletion(prompt) {
const [response, setResponse] = useState("");
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!prompt) return;
setLoading(true);
fetch("/api/zhipu-ai", {
method: "POST",
body: JSON.stringify({ prompt })
})
.then(res => res.json())
.then(data => {
setResponse(data.output);
setLoading(false);
});
}, [prompt]);
return { response, loading };
}
实际应用场景:
技术要点:
这10个模式,不是面试题,是真实项目中天天要用的核心能力:
为什么必须掌握? 因为这些模式正在成为行业标准。字节的Semi Design、阿里的Ant Design 5、Vercel的Next.js...所有主流框架都在用这套思路。不跟上,代码就会被吐槽"像5年前写的"。
💬 留言讨论: