文章目录
-
- [一、React核心组件:函数式组件 vs 类组件](#一、React核心组件:函数式组件 vs 类组件)
-
- [1. 类组件(Class Component)](#1. 类组件(Class Component))
- [2. 函数式组件(Functional Component)](#2. 函数式组件(Functional Component))
- [二、React组件传值:基础业务 vs 复杂业务](#二、React组件传值:基础业务 vs 复杂业务)
-
- [1. 基础业务传值(小型应用/局部组件通信)](#1. 基础业务传值(小型应用/局部组件通信))
- [2. 复杂业务传值(中大型应用/全局状态)](#2. 复杂业务传值(中大型应用/全局状态))
- [三、React最新钩子函数(React 18+)全场景应用](#三、React最新钩子函数(React 18+)全场景应用)
-
- [1. 基础钩子(必学)](#1. 基础钩子(必学))
- [2. 性能优化钩子](#2. 性能优化钩子)
- [3. React 18新增钩子(进阶)](#3. React 18新增钩子(进阶))
- 总结
- 思考
- 核心前提:先分清「浏览器任务队列」和「React调度系统」
- 一、逐个解析:钩子的本质与实现原理
-
- [1. useTransition:标记「低优先级更新」](#1. useTransition:标记「低优先级更新」)
- [2. useDeferredValue:延迟更新「非关键值」](#2. useDeferredValue:延迟更新「非关键值」)
- [3. useSyncExternalStore:同步外部状态到React](#3. useSyncExternalStore:同步外部状态到React)
- [二、关键补充:React Scheduler的底层实现](#二、关键补充:React Scheduler的底层实现)
- 总结
一、React核心组件:函数式组件 vs 类组件
1. 类组件(Class Component)
概念
基于ES6类语法实现的组件,是React早期的主流写法,通过继承React.Component实现,内部通过this.state管理状态,this.props接收外部传参,生命周期方法控制组件渲染时机。
核心特点
- 有完整的生命周期钩子(如
componentDidMount、componentDidUpdate) - 状态(state)是不可直接修改的对象,需通过
this.setState()更新 - 需绑定
this指向(常见问题:事件处理函数this丢失)
实战实例:类组件实现计数器
jsx
import React from 'react';
// 类组件定义
class ClassCounter extends React.Component {
// 1. 初始化状态
constructor(props) {
super(props); // 必须调用super接收props
this.state = {
count: 0, // 计数器初始值
title: props.title || '类组件计数器' // 接收外部传参
};
// 绑定this(解决事件函数this丢失问题)
this.handleIncrement = this.handleIncrement.bind(this);
}
// 2. 事件处理函数
handleIncrement() {
// 正确更新状态:setState是异步的,支持函数式更新(依赖旧状态)
this.setState(prevState => ({
count: prevState.count + 1
}));
}
// 3. 生命周期:组件挂载完成后执行(仅执行一次)
componentDidMount() {
console.log('类组件挂载完成');
// 模拟异步更新:3秒后自动加1
this.timer = setTimeout(() => {
this.setState({ count: this.state.count + 1 });
}, 3000);
}
// 4. 生命周期:组件卸载前执行(清理副作用)
componentWillUnmount() {
clearTimeout(this.timer); // 清除定时器,防止内存泄漏
console.log('类组件卸载');
}
// 5. 渲染函数(必须实现)
render() {
const { count, title } = this.state;
return (
<div style={{ padding: '20px', border: '1px solid #ccc' }}>
<h3>{title}</h3>
<p>当前计数:{count}</p>
<button onClick={this.handleIncrement}>点击加1</button>
</div>
);
}
}
// 使用组件
function App() {
return <ClassCounter title="我的第一个类组件计数器" />;
}
export default App;
代码解释
constructor:初始化状态和绑定this,是类组件的构造方法;setState:异步更新状态,函数式更新能避免"状态更新依赖旧值"的坑;- 生命周期:
componentDidMount用于初始化异步操作(如请求数据、定时器),componentWillUnmount用于清理副作用(如定时器、事件监听)。
2. 函数式组件(Functional Component)
概念
基于普通JavaScript函数实现的组件,React 16.8引入Hooks后成为主流写法,无需继承、无this指向问题,通过Hooks(如useState、useEffect)管理状态和生命周期。
核心特点
- 简洁:无类语法、无
this绑定问题 - 无生命周期方法:通过
useEffect模拟所有生命周期行为 - 状态管理:通过
useState、useReducer等Hooks实现 - 纯函数特性:输入(props)相同则输出(UI)相同(无副作用时)
实战实例:函数式组件实现计数器(对比类组件)
jsx
import React, { useState, useEffect } from 'react';
// 函数式组件定义(接收props参数)
function FunctionalCounter(props) {
// 1. 状态管理:useState(初始值) → [状态值, 状态更新函数]
const [count, setCount] = useState(0);
const [title] = useState(props.title || '函数式组件计数器');
// 2. 事件处理函数(无需绑定this)
const handleIncrement = () => {
// 状态更新:直接调用setCount,支持函数式更新
setCount(prevCount => prevCount + 1);
};
// 3. 模拟生命周期:useEffect(副作用函数, 依赖数组)
// 依赖数组为空 → 仅在组件挂载/卸载时执行(对应componentDidMount + componentWillUnmount)
useEffect(() => {
console.log('函数式组件挂载完成');
// 模拟异步更新
const timer = setTimeout(() => {
setCount(prev => prev + 1);
}, 3000);
// 清理函数(对应componentWillUnmount)
return () => {
clearTimeout(timer);
console.log('函数式组件卸载');
};
}, []); // 空依赖 → 仅执行一次
// 4. 渲染UI
return (
<div style={{ padding: '20px', border: '1px solid #ccc', marginTop: '10px' }}>
<h3>{title}</h3>
<p>当前计数:{count}</p>
<button onClick={handleIncrement}>点击加1</button>
</div>
);
}
// 使用组件
function App() {
return <FunctionalCounter title="我的第一个函数式组件计数器" />;
}
export default App;
代码解释
useState:替代类组件的this.state,返回"状态值+更新函数",支持直接更新或函数式更新;useEffect:替代类组件的生命周期,第一个参数是"副作用函数",第二个参数是依赖数组,数组为空时仅执行一次(挂载),返回的函数用于清理副作用(卸载);- 函数式组件无需处理
this,代码量比类组件少约30%,是React官方推荐的写法。
二、React组件传值:基础业务 vs 复杂业务
1. 基础业务传值(小型应用/局部组件通信)
适用于父子/兄弟/跨2-3层组件 通信,核心是props、回调函数、Context。
场景1:父传子(props)
场景:父组件传递"商品信息"给子组件展示
jsx
// 父组件
function ProductList() {
const products = [
{ id: 1, name: 'React实战教程', price: 99 },
{ id: 2, name: 'Vue实战教程', price: 89 }
];
return (
<div>
<h2>商品列表</h2>
{/* 父传子:通过props传递数据 */}
{products.map(product => (
<ProductItem key={product.id} product={product} />
))}
</div>
);
}
// 子组件(接收props)
function ProductItem(props) {
// 解构props,简化使用
const { product } = props;
return (
<div style={{ padding: '10px', border: '1px solid #eee' }}>
<p>商品名称:{product.name}</p>
<p>商品价格:¥{product.price}</p>
</div>
);
}
场景2:子传父(回调函数)
场景:子组件点击"加入购物车",通知父组件更新购物车数量
jsx
// 父组件
function ShoppingCart() {
const [cartCount, setCartCount] = useState(0);
// 定义回调函数(子组件调用)
const handleAddToCart = () => {
setCartCount(prev => prev + 1);
};
return (
<div>
<h2>购物车({cartCount})</h2>
{/* 父传子:传递回调函数 */}
<ProductItem onAddToCart={handleAddToCart} />
</div>
);
}
// 子组件
function ProductItem() {
return (
<div>
<p>React实战教程 ¥99</p>
{/* 子调用父的回调函数,实现子传父 */}
<button onClick={() => props.onAddToCart()}>加入购物车</button>
</div>
);
}
场景3:跨层级传值(useContext)
场景:App根组件的"用户信息"需要传递给深层的"个人中心"组件(避免props层层透传)
jsx
// 1. 创建Context(默认值可选)
const UserContext = React.createContext({ name: '游客', id: 0 });
// 根组件
function App() {
const user = { name: '张三', id: 1001 };
return (
{/* 2. 提供Context值 */}
<UserContext.Provider value={user}>
<div>
<Header /> {/* 层级1 */}
</div>
</UserContext.Provider>
);
}
// 层级1组件
function Header() {
return <UserCenter />; // 层级2
}
// 层级2组件(接收Context)
function UserCenter() {
// 3. 消费Context值
const user = useContext(UserContext);
return (
<div>
<h3>个人中心</h3>
<p>用户名:{user.name}</p>
<p>用户ID:{user.id}</p>
</div>
);
}
2. 复杂业务传值(中大型应用/全局状态)
适用于跨页面/跨模块 的全局状态管理,推荐轻量方案Zustand(中小应用)和工业级方案Redux(大型应用)。
场景1:Zustand(轻量全局状态)
适用场景:中大型应用,需要简单、无样板代码的全局状态管理(如购物车、用户信息、主题配置)。
步骤1:安装Zustand
bash
npm install zustand
步骤2:实战实例:全局购物车状态
jsx
// store/cartStore.js(创建全局store)
import { create } from 'zustand';
// 创建购物车store
const useCartStore = create((set, get) => ({
// 1. 状态
cartItems: [], // 购物车商品列表
// 2. 计算属性(派生状态)
get totalPrice() {
return get().cartItems.reduce((sum, item) => sum + item.price * item.quantity, 0);
},
// 3. 方法(修改状态)
addItem: (product) => set((state) => {
// 检查商品是否已存在
const existingItem = state.cartItems.find(item => item.id === product.id);
if (existingItem) {
// 已存在:更新数量
return {
cartItems: state.cartItems.map(item =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 }
: item
)
};
} else {
// 不存在:新增商品
return {
cartItems: [...state.cartItems, { ...product, quantity: 1 }]
};
}
}),
removeItem: (productId) => set((state) => ({
cartItems: state.cartItems.filter(item => item.id !== productId)
}))
}));
// 组件1:商品列表(添加商品到购物车)
function ProductList() {
const addItem = useCartStore(state => state.addItem);
const products = [{ id: 1, name: 'React教程', price: 99 }, { id: 2, name: 'Vue教程', price: 89 }];
return (
<div>
<h3>商品列表</h3>
{products.map(product => (
<div key={product.id} style={{ margin: '10px 0' }}>
<p>{product.name} ¥{product.price}</p>
<button onClick={() => addItem(product)}>加入购物车</button>
</div>
))}
</div>
);
}
// 组件2:购物车(展示/删除商品)
function ShoppingCart() {
// 消费store的状态和方法
const cartItems = useCartStore(state => state.cartItems);
const totalPrice = useCartStore(state => state.totalPrice);
const removeItem = useCartStore(state => state.removeItem);
return (
<div style={{ marginTop: '20px', border: '1px solid #ccc', padding: '10px' }}>
<h3>购物车(总价:¥{totalPrice})</h3>
{cartItems.length === 0 ? (
<p>购物车为空</p>
) : (
cartItems.map(item => (
<div key={item.id}>
<p>{item.name} × {item.quantity} ¥{item.price * item.quantity}</p>
<button onClick={() => removeItem(item.id)}>删除</button>
</div>
))
)}
</div>
);
}
// 根组件
function App() {
return (
<div>
<ProductList />
<ShoppingCart />
</div>
);
}
场景2:Redux(大型应用全局状态)
适用场景:超大型应用(如电商、后台系统),需要严格的状态管理规范、中间件支持(如异步请求、日志)。
步骤1:安装依赖
bash
npm install @reduxjs/toolkit react-redux
步骤2:实战实例:全局用户状态
jsx
// store/userSlice.js(创建Slice)
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
// 异步action:模拟请求用户信息
export const fetchUserInfo = createAsyncThunk(
'user/fetchUserInfo',
async (userId, { rejectWithValue }) => {
try {
// 模拟API请求
const res = await new Promise((resolve) => {
setTimeout(() => resolve({ id: userId, name: '张三', role: 'admin' }), 1000);
});
return res;
} catch (err) {
return rejectWithValue(err.message);
}
}
);
// 创建用户Slice
const userSlice = createSlice({
name: 'user',
initialState: {
info: null, // 用户信息
loading: false, // 加载状态
error: null // 错误信息
},
reducers: {
// 同步action:更新用户名
updateUserName: (state, action) => {
if (state.info) {
state.info.name = action.payload;
}
}
},
// 处理异步action
extraReducers: (builder) => {
builder
.addCase(fetchUserInfo.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(fetchUserInfo.fulfilled, (state, action) => {
state.loading = false;
state.info = action.payload;
})
.addCase(fetchUserInfo.rejected, (state, action) => {
state.loading = false;
state.error = action.payload || '请求失败';
});
}
});
// 导出同步action
export const { updateUserName } = userSlice.actions;
// 导出reducer
export default userSlice.reducer;
// store/index.js(配置Store)
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import userReducer from './userSlice';
// 创建store
const store = configureStore({
reducer: {
user: userReducer // 注册user reducer
}
});
// 根组件包裹Provider
function AppProvider({ children }) {
return <Provider store={store}>{children}</Provider>;
}
// 组件1:用户信息展示(消费Redux状态)
function UserInfo() {
// 从store获取状态
const { info, loading, error } = useSelector((state) => state.user);
// 获取dispatch方法
const dispatch = useDispatch();
// 触发异步请求
useEffect(() => {
dispatch(fetchUserInfo(1001));
}, [dispatch]);
// 触发同步更新
const handleUpdateName = () => {
dispatch(updateUserName('李四'));
};
if (loading) return <p>加载中...</p>;
if (error) return <p>错误:{error}</p>;
if (!info) return <p>未登录</p>;
return (
<div>
<h3>用户信息</h3>
<p>ID:{info.id}</p>
<p>姓名:{info.name}</p>
<p>角色:{info.role}</p>
<button onClick={handleUpdateName}>修改姓名为李四</button>
</div>
);
}
// 最终App组件
function App() {
return (
<AppProvider>
<UserInfo />
</AppProvider>
);
}
三、React最新钩子函数(React 18+)全场景应用
1. 基础钩子(必学)
| 钩子 | 核心作用 | 典型场景 | 实例代码 |
|---|---|---|---|
useState |
管理组件状态 | 计数器、表单输入、开关状态 | const [count, setCount] = useState(0); |
useEffect |
处理副作用(异步/生命周期) | 数据请求、定时器、事件监听 | 见"函数式计数器"的useEffect实例 |
useRef |
保存可变值/获取DOM元素 | 避免重复请求、获取输入框DOM | const inputRef = useRef(null); <input ref={inputRef} /> |
useContext |
跨层级获取Context值 | 全局主题、用户信息 | 见"跨层级传值"实例 |
2. 性能优化钩子
useMemo(缓存计算结果)
场景:避免复杂计算(如列表过滤、数据格式化)在每次渲染时重复执行
jsx
function ProductFilter({ products, keyword }) {
// 缓存过滤结果:仅当products或keyword变化时重新计算
const filteredProducts = useMemo(() => {
console.log('执行过滤计算'); // 仅依赖变化时打印
return products.filter(product =>
product.name.toLowerCase().includes(keyword.toLowerCase())
);
}, [products, keyword]); // 依赖数组
return (
<div>
{filteredProducts.map(product => (
<div key={product.id}>{product.name}</div>
))}
</div>
);
}
useCallback(缓存函数引用)
场景:传递给子组件的回调函数,避免子组件不必要的重渲染
jsx
// 父组件
function Parent() {
const [count, setCount] = useState(0);
// 缓存函数:仅当count变化时重新创建函数
const handleClick = useCallback(() => {
console.log('点击了', count);
}, [count]);
return <Child onClick={handleClick} />;
}
// 子组件(React.memo避免不必要重渲染)
const Child = React.memo(({ onClick }) => {
console.log('子组件渲染'); // 仅handleClick变化时打印
return <button onClick={onClick}>点击</button>;
});
3. React 18新增钩子(进阶)
useId(生成唯一ID)
场景:表单label与input关联、无障碍(a11y)属性、动态生成DOM的唯一标识
jsx
function FormInput() {
// 生成唯一ID
const inputId = useId();
return (
<div>
<label htmlFor={inputId}>用户名:</label>
<input id={inputId} type="text" />
</div>
);
}
useTransition(非阻塞更新)
场景:耗时操作(如大数据列表筛选),避免页面卡顿(优先级低于用户交互)
jsx
function BigList() {
const [keyword, setKeyword] = useState('');
const [list, setList] = useState([]);
// 标记过渡状态:isPending为true时显示加载提示
const [isPending, startTransition] = useTransition();
// 监听关键词变化,触发低优先级更新
useEffect(() => {
// startTransition包裹耗时操作,不阻塞UI
startTransition(() => {
// 模拟大数据筛选(10万条数据)
const filteredList = Array.from({ length: 100000 })
.map((_, i) => `Item ${i}`)
.filter(item => item.includes(keyword));
setList(filteredList);
});
}, [keyword]);
return (
<div>
<input
type="text"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
placeholder="搜索..."
/>
{isPending && <p>加载中...</p>}
<ul>
{list.map((item, i) => <li key={i}>{item}</li>)}
</ul>
</div>
);
}
useDeferredValue(延迟更新值)
场景 :与useTransition配合,延迟更新非关键UI(如搜索结果预览)
jsx
function Search() {
const [keyword, setKeyword] = useState('');
// 延迟更新keyword:优先响应用户输入,再更新结果
const deferredKeyword = useDeferredValue(keyword, { timeoutMs: 200 });
// 仅当deferredKeyword变化时筛选数据
const results = useMemo(() => {
return Array.from({ length: 1000 })
.map((_, i) => `Result ${i}: ${deferredKeyword}`);
}, [deferredKeyword]);
return (
<div>
<input
type="text"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
placeholder="输入搜索词..."
/>
<div>
{results.map((item, i) => <div key={i}>{item}</div>)}
</div>
</div>
);
}
useSyncExternalStore(同步外部状态)
场景:订阅外部数据源(如Redux、Zustand、浏览器API),兼容React并发模式
jsx
// 订阅浏览器窗口大小变化
function WindowSize() {
// 订阅外部状态(窗口大小)
const size = useSyncExternalStore(
// 1. 订阅函数:返回取消订阅的函数
(callback) => {
window.addEventListener('resize', callback);
return () => window.removeEventListener('resize', callback);
},
// 2. 获取当前状态的函数
() => ({ width: window.innerWidth, height: window.innerHeight }),
// 3. 服务端渲染的默认值
() => ({ width: 0, height: 0 })
);
return (
<div>
<p>窗口宽度:{size.width}px</p>
<p>窗口高度:{size.height}px</p>
</div>
);
}
总结
- 组件选型 :函数式组件+Hooks是React主流写法,类组件仅需了解(维护老项目),函数式组件无
this问题、代码更简洁; - 传值方案 :基础传值用
props/回调/Context(局部通信),复杂传值用Zustand(轻量)/Redux(大型应用),Zustand上手成本远低于Redux; - 钩子应用 :基础钩子(
useState/useEffect/useRef)是核心,性能优化用useMemo/useCallback,React 18新增钩子(useTransition/useDeferredValue)解决并发渲染下的卡顿问题。
通过以上实例和场景,你可以覆盖React从基础到实战的核心知识点,建议先掌握函数式组件和基础钩子,再逐步学习状态管理和进阶钩子,结合实际业务场景练习会更容易消化。
思考
React 18中的useTransition、useDeferredValue、useSyncExternalStore这三个钩子怎么理解与宏任务、微任务是否关联。
核心前提:先分清「浏览器任务队列」和「React调度系统」
要回答这个问题,首先要纠正一个关键认知:这三个钩子本身既不是宏任务,也不是微任务。
浏览器的宏/微任务是JS引擎层面的任务队列机制:
| 类型 | 包含内容 | 执行优先级 |
|---|---|---|
| 宏任务 | script、setTimeout、setInterval、MessageChannel、UI渲染等 | 低 |
| 微任务 | Promise.then、queueMicrotask、MutationObserver等 | 高(宏任务执行完立即执行) |
而useTransition等钩子是React 18并发渲染 特性的核心API,依赖的是React自身的「Scheduler(调度器)」系统------它基于浏览器的宏任务(主要是MessageChannel)实现,但本质是React对任务优先级的调度,而非直接对应浏览器的宏/微任务分类。
一、逐个解析:钩子的本质与实现原理
1. useTransition:标记「低优先级更新」
核心定位
不是任务类型,而是React内部的更新优先级标记,用于将「非紧急更新」(如大数据列表筛选、复杂计算)标记为「过渡更新(Transition)」,让「紧急更新」(如用户输入、点击)优先执行,避免页面卡顿。
实现原理
- 优先级划分 :React将更新分为两类:
- 紧急更新(Urgent):用户交互(输入、点击、滚动),必须同步执行,阻塞主线程;
- 过渡更新(Transition):
useTransition包裹的更新,低优先级,可中断、可延后。
- 底层调度 :
- React Scheduler(调度器)使用
MessageChannel创建宏任务(放弃了requestIdleCallback,因为触发时机不可控),为不同优先级的任务分配「过期时间(expiration time)」------过期时间越短,优先级越高。 - 当浏览器主线程空闲时,Scheduler会取出高优先级任务执行;如果低优先级任务执行中来了高优先级任务,低优先级任务会被中断,等高优先级任务执行完再恢复。
- React Scheduler(调度器)使用
- 关键效果 :
isPending状态会标识过渡更新是否在进行中,让你可以展示加载提示,且用户交互不会被阻塞。
场景实例(补充原理说明)
jsx
import { useState, useTransition } from 'react';
function BigList() {
const [keyword, setKeyword] = useState('');
const [list, setList] = useState([]);
const [isPending, startTransition] = useTransition();
useEffect(() => {
// 1. 这里的更新被标记为低优先级(Transition)
startTransition(() => {
// 模拟10万条数据筛选(耗时操作)
const filtered = Array.from({ length: 100000 })
.map((_, i) => `Item ${i}`)
.filter(item => item.includes(keyword));
// 2. 这个setList是低优先级更新,可被用户输入中断
setList(filtered);
});
}, [keyword]);
return (
<div>
{/* 3. 输入框的setKeyword是紧急更新,优先执行 */}
<input
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
placeholder="搜索..."
/>
{isPending && <p>加载中...</p>}
<ul>{list.map((item, i) => <li key={i}>{item}</li>)}</ul>
</div>
);
}
2. useDeferredValue:延迟更新「非关键值」
核心定位
useDeferredValue是useTransition的「值版本」,本质是让一个状态的更新滞后于紧急更新,而非直接关联宏/微任务。
实现原理
- 值的延迟同步 :当依赖的状态(如
keyword)变化时,React会先更新「紧急值」(输入框的keyword),然后在浏览器空闲时,异步更新deferredValue(延迟值)。 - 优先级复用 :底层复用React Scheduler的优先级调度机制,
deferredValue的更新优先级与useTransition的过渡更新一致,属于低优先级。 - 缓存与重渲染 :配合
useMemo使用时,可避免低优先级值变化导致的重复计算,进一步优化性能。
场景实例(补充原理说明)
jsx
import { useState, useDeferredValue, useMemo } from 'react';
function Search() {
const [keyword, setKeyword] = useState('');
// 1. deferredKeyword 会滞后于 keyword 更新(低优先级)
const deferredKeyword = useDeferredValue(keyword, { timeoutMs: 200 });
// 2. 仅当 deferredKeyword 变化时才重新筛选(避免输入时频繁计算)
const results = useMemo(() => {
return Array.from({ length: 1000 })
.map((_, i) => `Result ${i}: ${deferredKeyword}`);
}, [deferredKeyword]);
return (
<div>
{/* 3. 输入框的更新是紧急的,优先响应 */}
<input
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
/>
{/* 4. 结果列表基于延迟值渲染,不阻塞输入 */}
<div>{results.map((item, i) => <div key={i}>{item}</div>)}</div>
</div>
);
}
3. useSyncExternalStore:同步外部状态到React
核心定位
不是任务调度钩子,而是适配并发渲染的外部状态订阅工具,确保React组件能同步外部数据源(如Redux、Zustand、浏览器API)的状态,避免并发渲染下的数据不一致。
实现原理
- 订阅-取消订阅封装 :
- 接收三个参数:
subscribe(订阅外部状态)、getSnapshot(获取当前状态)、getServerSnapshot(服务端渲染默认值)。 - 当外部状态变化时,
subscribe中的回调会触发React组件更新,这个更新遵循React的优先级规则。
- 接收三个参数:
- 并发渲染适配 :
- 在React并发渲染(如更新被中断)时,
useSyncExternalStore会确保组件最终拿到的是最新的外部状态,而非中断前的旧状态。 - 它不直接操作宏/微任务,而是封装了订阅逻辑,让外部状态的更新能兼容React的调度机制。
- 在React并发渲染(如更新被中断)时,
场景实例(补充原理说明)
jsx
import { useSyncExternalStore } from 'react';
// 外部数据源:浏览器窗口大小(非React状态)
function subscribeToWindowSize(callback) {
window.addEventListener('resize', callback);
// 返回取消订阅函数(适配组件卸载)
return () => window.removeEventListener('resize', callback);
}
function getWindowSizeSnapshot() {
return { width: window.innerWidth, height: window.innerHeight };
}
function WindowSize() {
// 1. 同步外部状态到组件,兼容并发渲染
const size = useSyncExternalStore(
subscribeToWindowSize, // 订阅
getWindowSizeSnapshot, // 获取当前状态
() => ({ width: 0, height: 0 }) // 服务端默认值
);
return (
<div>
<p>宽度:{size.width}px</p>
<p>高度:{size.height}px</p>
</div>
);
}
二、关键补充:React Scheduler的底层实现
React的调度器(scheduler包)是实现上述钩子的核心,它的底层逻辑:
- 放弃requestIdleCallback:因为其触发时机不可控(只有浏览器空闲时才触发),且兼容性差。
- 使用MessageChannel创建宏任务 :
- MessageChannel的
port.postMessage()会创建一个宏任务,执行时机比setTimeout更精准(无4ms延迟)。 - Scheduler将不同优先级的任务放入队列,每次执行完一个任务后,检查是否有更高优先级的任务,若有则中断当前任务,优先执行高优先级任务。
- MessageChannel的
- 优先级与过期时间 :
- 每个任务都有一个「过期时间」(如紧急任务过期时间是0,过渡任务过期时间是500ms)。
- 过期时间越短,优先级越高;任务过期后,会被提升为紧急任务强制执行。
总结
- 核心结论 :
useTransition/useDeferredValue/useSyncExternalStore既不是宏任务也不是微任务,它们是React基于自身Scheduler(调度器)实现的优先级调度/状态同步工具,底层依赖MessageChannel(宏任务)实现调度,但本身不属于浏览器任务队列分类。 - useTransition/useDeferredValue:核心是标记低优先级更新,让用户交互(紧急更新)不被耗时操作阻塞,提升页面响应性。
- useSyncExternalStore:核心是适配React并发渲染,确保外部状态与组件状态同步,避免数据不一致问题。