乐闻世界logo
搜索文章和话题

React面试题手册

useCallback 和 useMemo 有什么区别?什么场景下使用?

问题背景useCallback 和 useMemo 是 React 提供的两个性能优化 Hook,它们看起来很相似,但用途和返回值有本质区别。核心区别语法对比// useCallback:返回函数本身const memoizedCallback = useCallback(() => { doSomething(a, b);}, [a, b]);// useMemo:返回函数执行结果const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);本质区别| 特性 | useCallback | useMemo ||------|-------------|---------|| 返回值 | 函数引用 | 任意值(计算结果) || 用途 | 缓存函数 | 缓存计算结果 || 类似于 | 函数记忆化 | 值记忆化 || 场景 | 避免函数重建 | 避免重复计算 |useCallback 详解基本用法const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b]);使用场景1. 传递给子组件避免不必要渲染function Parent({ items }) { // ❌ 每次渲染都创建新函数,导致 Child 重新渲染 const handleClick = () => { console.log("clicked"); }; // ✅ 使用 useCallback 缓存函数 const handleClick = useCallback(() => { console.log("clicked"); }, []); return <Child onClick={handleClick} items={items} />;}// 配合 React.memo 使用const Child = React.memo(({ onClick, items }) => { console.log("Child render"); return <button onClick={onClick}>Click</button>;});2. 作为 useEffect 依赖function UserProfile({ userId }) { // ❌ fetchUser 每次都是新引用,useEffect 每次都执行 const fetchUser = () => { api.getUser(userId); }; // ✅ 使用 useCallback 稳定函数引用 const fetchUser = useCallback(() => { api.getUser(userId); }, [userId]); useEffect(() => { fetchUser(); }, [fetchUser]);}useMemo 详解基本用法const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);使用场景1. 避免重复计算function ProductList({ products, filter }) { // ❌ 每次渲染都重新过滤 const filteredProducts = products.filter(p => p.name.includes(filter) ); // ✅ 只在 products 或 filter 变化时重新计算 const filteredProducts = useMemo(() => products.filter(p => p.name.includes(filter)), [products, filter] ); return <ul>{filteredProducts.map(p => <li key={p.id}>{p.name}</li>)}</ul>;}2. 复杂计算优化function DataTable({ data }) { // ✅ 大数据量排序只计算一次 const sortedData = useMemo(() => { console.log("Sorting..."); return [...data].sort((a, b) => a.score - b.score); }, [data]); // ✅ 复杂数据转换 const chartData = useMemo(() => { return data.reduce((acc, item) => { // 复杂聚合逻辑 return acc; }, {}); }, [data]); return <Chart data={chartData} />;}3. 引用稳定性function Parent({ items }) { // ❌ 每次渲染创建新对象,导致子组件重渲染 const style = { color: "red" }; // ✅ 保持对象引用稳定 const style = useMemo(() => ({ color: "red" }), []); return <Child style={style} />;}组合使用useCallback 本质上是 useMemo 的语法糖// useCallback 实现useCallback(fn, deps);// 等价于useMemo(() => fn, deps);配合使用优化性能function SearchResults({ query, data }) { // 缓存过滤结果 const results = useMemo(() => { return data.filter(item => item.name.toLowerCase().includes(query.toLowerCase()) ); }, [data, query]); // 缓存事件处理函数 const handleItemClick = useCallback((id) => { console.log("Selected:", id); }, []); // 缓存传递给子组件的 props const listProps = useMemo(() => ({ items: results, onItemClick: handleItemClick }), [results, handleItemClick]); return <ResultList {...listProps} />;}常见误区误区 1:过度使用// ❌ 不必要的优化,简单计算不需要 useMemoconst total = useMemo(() => a + b, [a, b]);// ✅ 直接计算即可const total = a + b;误区 2:忽略依赖// ❌ 闭包陷阱const multiplier = 2;const result = useMemo(() => value * multiplier, [value]);// ✅ 添加所有依赖const multiplier = 2;const result = useMemo(() => value * multiplier, [value, multiplier]);最佳实践先测量,后优化:使用 React DevTools Profiler 找出性能瓶颈简单计算不需要缓存:缓存本身也有开销配合 React.memo 使用:确保子组件实现了 shouldComponentUpdate正确声明依赖:遵循 ESLint 提示// 好的组合示例function UserList({ users, onSelect }) { // 缓存排序结果 const sortedUsers = useMemo( () => [...users].sort((a, b) => a.name.localeCompare(b.name)), [users] ); // 缓存事件处理 const handleSelect = useCallback( (userId) => onSelect(userId), [onSelect] ); return ( <ul> {sortedUsers.map(user => ( <UserItem key={user.id} user={user} onSelect={handleSelect} /> ))} </ul> );}
阅读 0·3月15日 00:43

React Query 中的 useQuery 和 useMutation 钩子有什么区别,分别适用于什么场景?

React Query 中的 useQuery 和 useMutation 是两个核心钩子,它们的区别和适用场景如下:useQuery功能:用于执行只读操作,如获取数据。适用场景:获取列表数据(如用户列表、产品列表)获取详情数据(如用户详情、订单详情)任何需要从服务器读取数据而不修改的场景特点:自动缓存数据支持数据失效和背景刷新返回数据、加载状态、错误状态等可以配置重试策略基本用法:const { data, isLoading, error } = useQuery('todos', fetchTodos);useMutation功能:用于执行修改操作,如创建、更新、删除数据。适用场景:提交表单数据更新用户信息删除资源任何需要修改服务器数据的场景特点:支持乐观更新可以配置成功/失败回调支持请求取消可以触发相关查询的重新获取基本用法:const mutation = useMutation(addTodo, { onSuccess: () => { // 成功回调,如重新获取数据 queryClient.invalidateQueries('todos'); },});// 调用方式mutation.mutate(newTodo);核心区别操作类型:useQuery 用于读取操作,useMutation 用于写入操作缓存行为:useQuery 自动缓存数据,useMutation 不缓存结果调用方式:useQuery 自动执行(可配置),useMutation 需要手动调用 mutate 方法返回值:useQuery 返回数据和状态,useMutation 返回 mutation 函数和状态正确使用这两个钩子可以有效地管理应用中的数据获取和修改操作,提高开发效率和用户体验。
阅读 0·3月7日 12:25

React Query 与传统状态管理库(如 Redux)的区别是什么,什么时候应该使用 React Query?

React Query 和传统状态管理库(如 Redux)在设计理念和使用场景上有显著区别:核心区别管理的状态类型:React Query:专门管理服务器状态(从API获取的数据)Redux:管理全局客户端状态(如用户偏好、UI状态、应用配置等)状态管理方式:React Query:声明式数据获取,自动处理缓存、失效、重试等Redux:需要手动编写action、reducer来管理状态更新缓存机制:React Query:内置强大的缓存系统,自动处理数据的缓存和失效Redux:没有内置缓存机制,需要手动实现或使用额外库数据获取:React Query:集成了数据获取逻辑,支持自动重试、轮询等Redux:需要手动集成axios/fetch等库,处理异步逻辑代码复杂度:React Query:减少了样板代码,API简洁直观Redux:需要编写更多样板代码(action types、actions、reducers)适用场景何时使用 React Query:需要频繁与API交互的应用:React Query 专门优化了服务器状态管理需要缓存数据的场景:内置缓存机制减少重复请求需要乐观更新的场景:提升用户体验需要处理分页、无限滚动的场景:内置支持需要自动重试和错误处理的场景:简化错误处理逻辑何时使用传统状态管理库:需要管理复杂的客户端状态:如多级嵌套的UI状态需要中间件支持:如日志、路由、持久化等需要时间旅行调试:Redux DevTools提供强大的调试能力需要严格的状态变更控制:如金融应用最佳实践实际上,React Query 和传统状态管理库并不是互斥的,它们可以结合使用:使用 React Query 管理所有服务器状态使用 Redux/Zustand 等管理客户端状态这样可以充分发挥各自的优势,构建更高效、可维护的应用React Query 的出现并不是要完全取代传统状态管理库,而是为了解决服务器状态管理这一特定领域的问题,让开发人员能够更专注于业务逻辑和UI开发。
阅读 0·3月7日 12:25

如何在 React Query 中处理错误和重试,有哪些最佳实践?

React Query 提供了强大的错误处理和重试机制,帮助开发者构建更健壮的应用:错误处理基本错误处理: const { data, error, isError } = useQuery('todos', fetchTodos); if (isError) { return <div>Error: {error.message}</div>; }全局错误处理:通过 QueryClient 配置 const queryClient = new QueryClient({ defaultOptions: { queries: { onError: (error) => { console.error('Query error:', error); // 可以在这里添加全局错误通知 }, }, }, });Mutation 错误处理: const mutation = useMutation(addTodo, { onError: (error, variables, context) => { console.error('Mutation error:', error); // 错误处理逻辑 }, });重试机制基本重试配置: const { data } = useQuery('todos', fetchTodos, { retry: 3, // 默认值为 3 });高级重试配置: const { data } = useQuery('todos', fetchTodos, { retry: (failureCount, error) => { // 自定义重试逻辑 if (error.status === 404) return false; // 404 不重试 if (failureCount >= 3) return false; // 最多重试 3 次 return true; }, retryDelay: (attemptIndex) => { // 指数退避策略 return Math.min(1000 * 2 ** attemptIndex, 30000); }, });最佳实践错误边界:使用 React 错误边界捕获查询错误 <ErrorBoundary fallback={<ErrorComponent />}> <QueryComponent /> </ErrorBoundary>错误状态 UI:为不同类型的错误提供不同的 UI 反馈 if (isError) { if (error.status === 401) { return <div>请先登录</div>; } else if (error.status === 404) { return <div>资源不存在</div>; } else { return <div>发生错误:{error.message}</div>; } }重试策略:对网络错误使用重试对 5xx 服务器错误使用重试对 4xx 客户端错误(如 401、404)不使用重试错误日志:集成错误监控服务(如 Sentry) const queryClient = new QueryClient({ defaultOptions: { queries: { onError: (error) => { Sentry.captureException(error); }, }, }, });用户反馈:显示加载状态显示错误信息提供重试按钮乐观更新错误处理:在 mutation 中正确处理回滚 const mutation = useMutation(updateTodo, { onMutate: () => { /* 乐观更新 */ }, onError: (error, variables, context) => { // 回滚数据 queryClient.setQueryData('todos', context.previousTodos); // 显示错误信息 showNotification('更新失败', 'error'); }, });通过合理配置错误处理和重试机制,可以显著提高应用的可靠性和用户体验。
阅读 0·3月7日 12:25

React Query 有哪些高级特性,如依赖查询、并行查询和窗口聚焦重新获取?

React Query 提供了许多高级特性,使数据管理更加灵活和强大:1. 依赖查询(Dependent Queries)依赖查询是指一个查询的执行依赖于另一个查询的结果。使用场景:当你需要先获取一个资源的 ID,然后用这个 ID 获取详细信息时。实现方式:const { data: user } = useQuery(['user', userId], fetchUser);// 只有当 user 存在时才执行第二个查询const { data: userPosts } = useQuery( ['posts', user?.id], () => fetchPosts(user.id), { enabled: !!user });2. 并行查询(Parallel Queries)并行查询是指同时执行多个独立的查询。使用场景:当你需要在一个组件中获取多个不相关的数据时。实现方式:// 方式1:多个 useQuery 钩子const { data: users } = useQuery('users', fetchUsers);const { data: posts } = useQuery('posts', fetchPosts);// 方式2:使用 useQueries 钩子(React Query v3+)const results = useQueries([ { queryKey: ['users'], queryFn: fetchUsers }, { queryKey: ['posts'], queryFn: fetchPosts },]);3. 窗口聚焦重新获取(Window Focus Refetching)当用户重新聚焦浏览器窗口时,React Query 可以自动重新获取数据。使用场景:确保用户看到的是最新数据,特别是在多标签页应用中。配置方式:// 全局配置const queryClient = new QueryClient({ defaultOptions: { queries: { refetchOnWindowFocus: true, // 默认值 }, },});// 单个查询配置const { data } = useQuery('todos', fetchTodos, { refetchOnWindowFocus: false, // 禁用});4. 分页和无限滚动(Pagination & Infinite Scroll)React Query 提供了专门的钩子来处理分页和无限滚动。实现方式:// 分页查询const { data, fetchNextPage, hasNextPage } = useInfiniteQuery( ['posts', pageSize], ({ pageParam = 1 }) => fetchPosts(pageParam, pageSize), { getNextPageParam: (lastPage, pages) => { return lastPage.hasMore ? pages.length + 1 : undefined; }, });5. 轮询(Polling)自动定期重新获取数据。使用场景:需要显示实时数据的应用,如仪表盘、聊天应用等。配置方式:const { data } = useQuery('todos', fetchTodos, { refetchInterval: 5000, // 每5秒重新获取 refetchIntervalInBackground: true, // 后台也轮询});6. 预取(Prefetching)提前获取可能需要的数据,提升用户体验。实现方式:// 手动预取queryClient.prefetchQuery('todos', fetchTodos);// 视口预取(使用 react-query/prefetch)usePrefetchQuery('todos', fetchTodos);7. 持久化缓存(Persisted Queries)将缓存数据持久化到 localStorage 或其他存储中。实现方式:使用 persistQueryClient 插件这些高级特性使得 React Query 能够应对各种复杂的数据获取场景,提供更灵活、更高效的数据管理方案。
阅读 0·3月7日 12:25

React Query 如何与 React Suspense 集成使用,有哪些注意事项?

React Query 支持与 React Suspense 集成,使得数据获取可以像处理组件渲染一样自然:基本集成方法启用 Suspense 模式: const { data } = useQuery('todos', fetchTodos, { suspense: true, });使用 Suspense 组件包裹: function TodoList() { const { data: todos } = useQuery('todos', fetchTodos, { suspense: true, }); return ( <ul> {todos.map(todo => ( <li key={todo.id}>{todo.title}</li> ))} </ul> ); } function App() { return ( <Suspense fallback={<div>Loading...</div>}> <TodoList /> </Suspense> ); }错误边界处理当使用 Suspense 模式时,错误需要通过 Error Boundary 捕获:class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false, error: null }; } static getDerivedStateFromError(error) { return { hasError: true, error }; } render() { if (this.state.hasError) { return <div>Error: {this.state.error.message}</div>; } return this.props.children; }}function App() { return ( <ErrorBoundary> <Suspense fallback={<div>Loading...</div>}> <TodoList /> </Suspense> </ErrorBoundary> );}注意事项兼容性:Suspense 模式需要 React 16.6+ 版本确保所有相关组件都支持 Suspense错误处理:必须使用 Error Boundary 捕获错误不能再使用 useQuery 返回的 error 和 isError 属性缓存行为:即使数据在缓存中,首次渲染时仍会触发 Suspense后续渲染会直接使用缓存数据,不会触发 Suspense性能考虑:Suspense 模式可能会导致组件树的多次渲染对于复杂应用,需要权衡用户体验和性能与其他功能的兼容性:窗口聚焦重新获取:可能会导致意外的 Suspense 触发轮询:需要谨慎配置,避免频繁触发 Suspense服务器端渲染:在 SSR 环境中使用 Suspense 需要特殊处理推荐使用 dehydrate 和 hydrate 方法最佳实践渐进式采用:先在非关键组件中尝试 Suspense 模式逐步扩展到整个应用合理使用 fallback:提供有意义的加载状态避免使用过于简单的加载指示器结合预取:使用 queryClient.prefetchQuery 提前获取数据减少 Suspense 的触发次数错误边界策略:在适当的层级放置 Error Boundary提供具体的错误信息和恢复选项通过合理集成 React Query 和 Suspense,可以创建更加流畅、响应迅速的用户界面,同时保持代码的简洁性和可读性。
阅读 0·3月7日 12:25

如何在 Zustand 中优化状态更新和性能?

Zustand 中的性能优化方法:选择性订阅:// 不推荐:订阅整个 store,会导致组件在任何状态变化时都重渲染const { count, user } = useStore();// 推荐:只订阅需要的状态部分const count = useStore((state) => state.count);const user = useStore((state) => state.user);使用 shallow 比较(对于复杂对象):import { create } from 'zustand';import { shallow } from 'zustand/shallow';// 订阅多个状态并使用 shallow 比较const { count, user } = useStore( (state) => ({ count: state.count, user: state.user }), shallow // 只有当 count 或 user 真正变化时才重渲染);状态拆分:// 按功能拆分多个 store// userStore.jsconst useUserStore = create((set) => ({ user: null, setUser: (user) => set({ user })}));// counterStore.jsconst useCounterStore = create((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 }))}));使用 get 访问当前状态(避免闭包陷阱):const useStore = create((set, get) => ({ count: 0, // 推荐:使用 get 获取最新状态 increment: () => set((state) => ({ count: state.count + 1 })), // 也可以使用 get incrementAsync: async () => { await someAsyncOperation(); set({ count: get().count + 1 }); }}));批量更新:// Zustand 会自动批量处理多个 set 调用const updateMultiple = () => { set({ count: 1 }); set({ user: { name: 'John' } }); // 只会触发一次重渲染};避免在组件渲染时创建新函数:// 不推荐:每次渲染都创建新函数const incrementBy = (value) => useStore.getState().incrementBy(value);// 推荐:在 store 中定义方法// 在 store 中:incrementBy: (value) => set((state) => ({ count: state.count + value }))// 在组件中:const incrementBy = useStore((state) => state.incrementBy);关键点:选择性订阅是 Zustand 性能优化的核心使用 shallow 比较可以优化复杂对象的订阅状态拆分可以减少不必要的重渲染合理使用 get 可以避免闭包陷阱Zustand 会自动处理批量更新
阅读 0·3月7日 12:01

如何在 Zustand 中创建自定义中间件?

在 Zustand 中创建自定义中间件非常灵活,可以用来实现各种功能,如日志记录、状态验证、性能监控等。基本自定义中间件结构:const customMiddleware = (config) => (set, get, api) => { // 在原始 store 之前执行的逻辑 const originalSet = set; // 包装 set 函数 const wrappedSet = (partial, replace) => { // 在状态更新前执行逻辑 console.log('State will update:', partial); // 调用原始 set const result = originalSet(partial, replace); // 在状态更新后执行逻辑 console.log('State updated:', get()); return result; }; // 创建 store const store = config(wrappedSet, get, api); // 返回增强后的 store return store;};示例 1:日志中间件const loggerMiddleware = (config) => (set, get, api) => { const originalSet = set; const wrappedSet = (partial, replace) => { const previousState = get(); const result = originalSet(partial, replace); const nextState = get(); console.log('Previous state:', previousState); console.log('Action:', partial); console.log('Next state:', nextState); return result; }; return config(wrappedSet, get, api);};// 使用const useStore = create( loggerMiddleware((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })) })));示例 2:状态验证中间件const validationMiddleware = (schema) => (config) => (set, get, api) => { const originalSet = set; const wrappedSet = (partial, replace) => { // 验证状态更新 const newState = typeof partial === 'function' ? partial(get()) : partial; const validation = schema.safeParse({ ...get(), ...newState }); if (!validation.success) { console.error('State validation failed:', validation.error); throw new Error('Invalid state update'); } return originalSet(partial, replace); }; return config(wrappedSet, get, api);};// 使用import { z } from 'zod';const storeSchema = z.object({ count: z.number().min(0), user: z.object({ id: z.string(), name: z.string().min(1) }).nullable()});const useStore = create( validationMiddleware(storeSchema)((set) => ({ count: 0, user: null, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: Math.max(0, state.count - 1) })) })));示例 3:性能监控中间件const performanceMiddleware = (config) => (set, get, api) => { const originalSet = set; const renderCounts = {}; const wrappedSet = (partial, replace) => { const startTime = performance.now(); const result = originalSet(partial, replace); const endTime = performance.now(); const duration = endTime - startTime; if (duration > 10) { console.warn(`Slow state update: ${duration.toFixed(2)}ms`, partial); } return result; }; const store = config(wrappedSet, get, api); // 跟踪组件渲染次数 const originalSubscribe = api.subscribe; api.subscribe = (listener, selector) => { const wrappedListener = (state, previousState) => { const key = selector ? selector.toString() : 'full-store'; renderCounts[key] = (renderCounts[key] || 0) + 1; if (renderCounts[key] % 10 === 0) { console.log(`Render count for ${key}:`, renderCounts[key]); } listener(state, previousState); }; return originalSubscribe(wrappedListener, selector); }; return store;};// 使用const useStore = create( performanceMiddleware((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })) })));示例 4:撤销/重做中间件const undoRedoMiddleware = (config) => (set, get, api) => { let history = []; let future = []; const MAX_HISTORY = 50; const originalSet = set; const wrappedSet = (partial, replace) => { const previousState = get(); const result = originalSet(partial, replace); const nextState = get(); // 保存到历史记录 history.push(previousState); if (history.length > MAX_HISTORY) { history.shift(); } // 清空未来记录 future = []; return result; }; const store = config(wrappedSet, get, api); // 添加撤销功能 store.undo = () => { if (history.length === 0) return; const previousState = history.pop(); future.push(get()); originalSet(previousState, true); }; // 添加重做功能 store.redo = () => { if (future.length === 0) return; const nextState = future.pop(); history.push(get()); originalSet(nextState, true); }; // 清空历史 store.clearHistory = () => { history = []; future = []; }; return store;};// 使用const useStore = create( undoRedoMiddleware((set, get) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })) })));// 在组件中使用function Counter() { const { count, increment, decrement } = useStore(); const undo = useStore((state) => state.undo); const redo = useStore((state) => state.redo); return ( <div> <h1>Count: {count}</h1> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> <button onClick={undo}>Undo</button> <button onClick={redo}>Redo</button> </div> );}关键点:自定义中间件是一个高阶函数,接收 config 并返回新的配置函数可以包装 set、get 和 api 来增强功能中间件的执行顺序很重要,通常外层中间件先执行可以在中间件中添加额外的功能,如日志、验证、性能监控等中间件可以返回增强后的 store,添加新的方法或属性
阅读 0·3月7日 12:01

如何在 Zustand 中处理异步操作?

在 Zustand 中处理异步操作的方法:基本异步操作:import { create } from 'zustand';const useStore = create((set, get) => ({ // 状态 user: null, loading: false, error: null, // 异步操作 fetchUser: async (userId) => { try { set({ loading: true, error: null }); const response = await fetch(`/api/users/${userId}`); const userData = await response.json(); set({ user: userData, loading: false }); } catch (err) { set({ error: err.message, loading: false }); } }, // 另一种方式:使用 get 获取最新状态 updateUserProfile: async (updates) => { try { set({ loading: true, error: null }); const currentUser = get().user; const response = await fetch(`/api/users/${currentUser.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updates) }); const updatedUser = await response.json(); set({ user: updatedUser, loading: false }); } catch (err) { set({ error: err.message, loading: false }); } }}));使用 Promise 链:const useStore = create((set) => ({ data: null, status: 'idle', // idle, loading, success, error fetchData: () => { set({ status: 'loading' }); return fetch('/api/data') .then((response) => response.json()) .then((data) => { set({ data, status: 'success' }); return data; }) .catch((error) => { set({ error: error.message, status: 'error' }); throw error; }); }}));结合 React Query 或 SWR:// 可以在 Zustand 中存储查询结果,同时使用 React Query 处理缓存和失效import { create } from 'zustand';import { useQuery } from 'react-query';const useStore = create((set) => ({ user: null, setUser: (user) => set({ user })}));// 在组件中function UserProfile({ userId }) { const { data, isLoading, error } = useQuery( ['user', userId], () => fetch(`/api/users/${userId}`).then(res => res.json()) ); // 当查询成功时,更新 Zustand store React.useEffect(() => { if (data) { useStore.getState().setUser(data); } }, [data]); // 使用 Zustand 中的用户数据 const user = useStore(state => state.user); return ( <div> {isLoading && <p>Loading...</p>} {error && <p>Error: {error.message}</p>} {user && <p>User: {user.name}</p>} </div> );}关键点:Zustand 支持直接在 store 方法中使用 async/await可以在异步操作中管理 loading 和 error 状态使用 get() 获取最新状态,避免闭包陷阱可以返回 Promise 以便在组件中处理异步操作的结果可以与 React Query 或 SWR 等库结合使用,获得更好的缓存和失效策略
阅读 0·3月7日 11:48