You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React TypeScript:如何让API调用结果在多组件中复用

React/TypeScript 跨组件共享API数据的标准实现方式

首先明确:你可以把API调用逻辑抽离到单独文件夹统一管理,但不能直接导入请求结果——因为API请求是异步操作,你导出的只能是请求函数,不是已经拿到的现成数据。至于要不要重复发起请求,完全可以通过方案避免,下面是几种行业内的标准实现方式:

1. 先把API请求逻辑抽离到单独模块

第一步先把所有API请求代码集中到一个地方,比如创建src/services/api.ts:

// src/services/api.ts
// 定义数据类型(TypeScript必备)
type User = {
  id: number;
  name: string;
  email: string;
};

// 封装API请求函数
export const fetchUserInfo = async (): Promise<User> => {
  const response = await fetch('https://your-api-domain.com/user/profile');
  if (!response.ok) {
    throw new Error(`请求失败:${response.status}`);
  }
  return response.json();
};

这样所有需要调用这个接口的组件,都可以导入这个函数,不用重复写请求逻辑。但直接在多个组件里调用fetchUserInfo()的话,每个组件都会发起独立请求,这时候就需要结合状态管理或缓存来复用数据。

2. 避免重复请求的核心方案

方案一:React Context + useReducer(轻量全局状态)

把请求到的数据存在全局Context里,所有组件直接从Context读取,整个应用只请求一次。
示例代码:

// src/contexts/UserContext.tsx
import { createContext, useContext, useEffect, useReducer } from 'react';
import { fetchUserInfo, User } from '../services/api';

// 定义状态类型
type UserState = {
  data: User | null;
  loading: boolean;
  error: string | null;
};

// 定义操作类型
type UserAction = 
  | { type: 'START_LOADING' }
  | { type: 'FETCH_SUCCESS'; payload: User }
  | { type: 'FETCH_ERROR'; payload: string };

// 初始状态
const initialState: UserState = {
  data: null,
  loading: false,
  error: null,
};

// 状态更新器
const userReducer = (state: UserState, action: UserAction): UserState => {
  switch (action.type) {
    case 'START_LOADING':
      return { ...state, loading: true, error: null };
    case 'FETCH_SUCCESS':
      return { ...state, loading: false, data: action.payload };
    case 'FETCH_ERROR':
      return { ...state, loading: false, error: action.payload };
    default:
      return state;
  }
};

// 创建Context
const UserContext = createContext<{ state: UserState } | undefined>(undefined);

// 提供状态的Provider组件
export const UserProvider = ({ children }: { children: React.ReactNode }) => {
  const [state, dispatch] = useReducer(userReducer, initialState);

  // 组件挂载时请求一次数据
  useEffect(() => {
    const loadUserInfo = async () => {
      dispatch({ type: 'START_LOADING' });
      try {
        const userData = await fetchUserInfo();
        dispatch({ type: 'FETCH_SUCCESS', payload: userData });
      } catch (err) {
        dispatch({ type: 'FETCH_ERROR', payload: (err as Error).message });
      }
    };
    loadUserInfo();
  }, []);

  return (
    <UserContext.Provider value={{ state }}>
      {children}
    </UserContext.Provider>
  );
};

// 自定义Hook,方便组件获取状态
export const useUser = () => {
  const context = useContext(UserContext);
  if (!context) {
    throw new Error('useUser必须在UserProvider组件内部使用');
  }
  return context.state;
};

然后在根组件App.tsx里包裹所有需要共享数据的组件:

// src/App.tsx
import { UserProvider } from './contexts/UserContext';
import ProfileCard from './components/ProfileCard';
import UserSettings from './components/UserSettings';

function App() {
  return (
    <UserProvider>
      <ProfileCard />
      <UserSettings />
    </UserProvider>
  );
}

export default App;

最后在组件里直接使用:

// src/components/ProfileCard.tsx
import { useUser } from '../contexts/UserContext';

export default function ProfileCard() {
  const { data, loading, error } = useUser();

  if (loading) return <div>加载中...</div>;
  if (error) return <div>出错了:{error}</div>;
  
  return (
    <div className="profile-card">
      <h2>{data?.name}</h2>
      <p>{data?.email}</p>
    </div>
  );
}

这样ProfileCard和UserSettings都会共享同一份用户数据,不会重复请求。

方案二:使用SWR/React Query(专业数据缓存库)

如果你的项目涉及大量API请求、需要缓存过期、自动重试等复杂逻辑,直接用这类成熟库更省心,它们内置了缓存、去重、背景刷新等功能。
以SWR为例,先安装依赖:

npm install swr

然后组件里直接使用:

// src/components/ProfileCard.tsx
import useSWR from 'swr';
import { fetchUserInfo, User } from '../services/api';

export default function ProfileCard() {
  // 第一个参数是缓存键,相同键会复用缓存
  const { data, error, isLoading } = useSWR<User>('user-profile', fetchUserInfo);

  if (isLoading) return <div>加载中...</div>;
  if (error) return <div>出错了:{error.message}</div>;
  
  return (
    <div className="profile-card">
      <h2>{data?.name}</h2>
      <p>{data?.email}</p>
    </div>
  );
}

另一个组件UserSettings用同样的缓存键调用,就会直接用缓存数据,不会发起新请求:

// src/components/UserSettings.tsx
import useSWR from 'swr';
import { fetchUserInfo, User } from '../services/api';

export default function UserSettings() {
  const { data, error, isLoading } = useSWR<User>('user-profile', fetchUserInfo);

  if (isLoading) return <div>加载中...</div>;
  if (error) return <div>出错了:{error.message}</div>;
  
  return (
    <div className="settings">
      <label>用户名:<input defaultValue={data?.name} /></label>
      <label>邮箱:<input defaultValue={data?.email} /></label>
    </div>
  );
}

方案三:Props层级传递(仅适用于简单场景)

如果数据只在少数几个层级浅的组件间共享,也可以在最上层的父组件里请求数据,然后通过props传递给子组件。但如果组件层级很深,这种方式会导致“props drilling”(层层传递props),代码维护性很差,不推荐复杂场景。

总结

  • API逻辑一定要抽离:统一放在services或api文件夹,方便后续修改、添加拦截器等操作;
  • 不能直接导入请求结果:异步请求的结果是动态获取的,你只能导入请求函数;
  • 避免重复请求的标准选择:
    • 轻量小项目:React Context + useReducer;
    • 中大型项目或复杂API场景:SWR/React Query;
    • 极简单的层级场景:Props传递。

内容的提问来源于stack exchange,提问作者jm22b

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 02:47:04