ReactJS

Provider

React Provider 解决的问题、使用方式和注意事项。

发布于 2026年5月30日0 views

Provider 通常指 React Context 的提供者,用来在一棵组件树里共享数据。

通俗地说:如果很多层子组件都需要同一份数据,不想一层一层通过 props 往下传,就可以用 Context + Provider。

解决什么问题

假设组件结构是这样:

Page
└── UserPanel
    └── UserToolbar
        └── UserName

如果 UserName 需要用户信息,而用户信息在 Page,就要这样传:

<UserPanel user={user} />

然后 UserPanel 再传给 UserToolbarUserToolbar 再传给 UserName

这种层层传递叫 prop drilling。层级少时没问题,层级多了会很难维护。

Provider 可以让深层组件直接读取上下文:

<UserProvider user={user}>
  <UserPanel />
</UserProvider>

深层组件:

const user = useUserContext();

使用方式

先创建 Context:

import { createContext, useContext } from "react";
import type { ReactNode } from "react";

type User = {
  id: string;
  name: string;
};

type UserContextValue = {
  user: User;
};

const UserContext = createContext<UserContextValue | null>(null);

创建 Provider:

export function UserProvider({
  user,
  children,
}: {
  user: User;
  children: ReactNode;
}) {
  return (
    <UserContext.Provider value={{ user }}>
      {children}
    </UserContext.Provider>
  );
}

封装读取 Hook:

export function useUserContext() {
  const context = useContext(UserContext);

  if (!context) {
    throw new Error("useUserContext must be used within UserProvider");
  }

  return context;
}

使用:

export function Page() {
  const user = { id: "1", name: "Alice" };

  return (
    <UserProvider user={user}>
      <UserPanel />
    </UserProvider>
  );
}

子组件读取:

export function UserName() {
  const { user } = useUserContext();

  return <span>{user.name}</span>;
}

适合使用的场景

适合:

  • 主题、语言、当前用户这类应用级信息。
  • 当前页面内多个组件共享筛选条件。
  • 一个功能模块内部共享选中项、编辑模式、步骤状态。
  • 多层组件都依赖同一份上下文。

不太适合:

  • 只有父子两层传值。
  • 单个组件内部状态。
  • 所有接口数据缓存。
  • 高频变化的大对象,比如输入框每个字符都更新的大表单。

注意事项

Provider 的 value 变化时,读取这个 Context 的组件都会重新渲染。

下面这种写法每次父组件渲染都会创建新对象:

<UserContext.Provider value={{ user, logout }}>
  {children}
</UserContext.Provider>

如果 Provider 内部还有状态,或者 value 传给很多组件,可以用 useMemo 稳定 value:

const value = useMemo(() => {
  return { user, logout };
}, [user, logout]);

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

如果 logout 是函数,也可以配合 useCallback

const logout = useCallback(() => {
  clearToken();
}, []);

不要把 Provider 当万能 store

Provider 很适合共享“局部上下文”,但不适合无限扩张。

如果状态跨很多页面、需要持久化、需要在任意地方读写,可以考虑 Zustand、Redux 这类状态库。

如果是接口数据,比如列表、详情、分页,优先考虑 TanStack Query / SWR,而不是全部塞进 Provider。

常见文件组织

可以把 Provider 放在:

src/providers/
└── UserProvider.tsx

或者放在具体功能模块旁边:

src/components/feature/users/
├── UserList.tsx
├── UserListProvider.tsx
└── useUserListContext.ts

如果只有某个功能模块使用,放在功能目录附近更容易维护。

总结

Provider 解决的是“多层组件共享同一份数据,不想层层传 props”的问题。它适合页面或功能模块上下文,但不要把所有状态和接口数据都塞进去;使用时注意 value 变化会触发消费组件重新渲染。