使用 React 上下文在组件之间共享数据

149 阅读4分钟

使用 React 上下文在组件之间共享数据

在 React 中,上下文 (Context) 是一种用于在组件之间共享数据的机制。通过使用上下文,我们可以避免将数据通过多层组件进行传递,而是直接在需要的组件中访问共享的数据。

在本文中,我们将介绍如何使用 React 上下文来在组件之间共享数据。我们将创建一个示例,展示如何使用上下文来共享一个数据对象,并在需要的组件中进行更新。

首先,我们需要创建一个上下文对象。我们将使用 createContext 函数来创建上下文对象,并定义上下文的类型。

import React, { useContext, createContext, useState, ReactNode, useCallback } from 'react';

// 定义数据对象的类型
export type IData = { tokenName?: number | string, tokenName2?: number | string };

// 定义上下文对象的类型
type ILayoutContext = { data: IData; updateData?: (key: keyof IData, value: IData[keyof IData]) => void } & IData;

// 创建上下文对象
export const LayoutContext = createContext<ILayoutContext>({
  data: {},
  updateData: (key: keyof IData, value: IData[keyof IData]) => {
    console.log(key, value, 'key,value');
    return { key, value };
  },
});

在上述代码中,我们定义了数据对象的类型 IData,用于表示我们要共享的数据结构。然后,我们定义了上下文对象的类型 ILayoutContext,它包含了 data 属性和一个可选的 updateData 方法,用于更新数据。我们使用 createContext 函数创建了一个名为 LayoutContext 的上下文对象,并通过 createContext 的参数设置了上下文的初始值。

接下来,我们可以使用 useContext 钩子函数来在组件中访问上下文对象的值。

export const useLayoutContext = (): ILayoutContext => {
  return useContext(LayoutContext);
};

上述代码定义了一个自定义钩子函数 useLayoutContext,它使用 useContext 函数来获取 LayoutContext 上下文对象的值,并将其类型设置为 ILayoutContext。我们可以在需要的组件中使用 useLayoutContext 钩子函数来获取上下文对象的值。

接下来,我们需要创建一个提供器组件,用于将数据提供给需要的组件,并提供更新数据的方法。

type IProvider = { children: ReactNode };

export const LayoutProvider: React.FC<IProvider> = ({ children }) => {
  const [data, setData] = useState<IData>({});

  const handleUpdate = useCallback((key: keyof IData, value: IData[keyof IData]) => {
    setData((prevData) => ({
      ...prevData,
      [key]: value,
    }));
  }, []);

  return <LayoutContext.Provider value={{ data, updateData: handleUpdate }}>{children}</LayoutContext.Provider>;
};

在上述代码中,我们创建了一个名为 LayoutProvider 的提供器组件。它接受一个 children 属性作为子组件,并使用 useState 钩子来定义一个名为 data 的状态,用于存储共享的数据对象。我们使用 useCallback 函数定义了一个名为 handleUpdate 的回调函数,用于更新数据对象。在 LayoutProvider 组件中,我们将 datahandleUpdate 方法作为值传递给 LayoutContext.Provider 组件,以便在子组件中访问和更新数据。

现在,我们已经定义了上下文对象和提供器组件,我们可以在需要的组件中使用它们了。

import React from 'react';

import { useLayoutContext, LayoutProvider } from './LayoutContext';

const MyComponent: React.FC = () => {
  const { data, updateData } = useLayoutContext();

  const handleClick = () => {
    updateData('tokenName', 'new value');
  };

  return (
    <div>
      <h2>MyComponent</h2>
      <p>Data: {data.tokenName}</p>
      <button onClick={handleClick}>Update Data</button>
    </div>
  );
};

const App: React.FC = () => {
  return (
    <LayoutProvider>
      <MyComponent />
    </LayoutProvider>
  );
};

export default App;

在上述代码中,我们导入了 useLayoutContext 钩子函数和 LayoutProvider 组件。在 MyComponent 组件中,我们使用 useLayoutContext 钩子函数来获取上下文对象的值和更新方法。我们在组件中展示了共享数据的值,并提供了一个按钮来触发更新数据的操作。

App 组件中,我们将 MyComponent 组件包裹在 LayoutProvider 组件中,以便将数据提供给 MyComponent 组件。

通过以上步骤,我们成功地创建了一个使用 React 上下文在组件之间共享数据的示例。在这个示例中,我们定义了一个上下文对象和提供器组件,以及一个自定义钩子函数来访问上下文对象的值。我们还展示了如何在组件中使用上下文对象的值和更新方法。

希望本文能帮助您理解如何使用 React 上下文在组件之间共享数据!

完整代码

import React, { useContext, createContext, useState, ReactNode, useCallback } from 'react'

export type IData = { tokenName?: number | string,tokenName2?: number | string }
type ILayoutContext = { data: IData; updateData?: (key: keyof IData, value: IData[keyof IData]) => void } & IData
type IProvider = { children: ReactNode }

export const LayoutContext = createContext<ILayoutContext>({
  data: { },
  updateData: (key: keyof IData, value: IData[keyof IData]) => {
    console.log(key, value,'key,value')

    return { key, value }
  },
})

export const useLayoutContext = (): ILayoutContext => {
  return useContext(LayoutContext)
}

export const LayoutProvider: React.FC<IProvider> = ({ children }) => {
  const [data, setData] = useState<IData>({ })

  const handleUpdate = useCallback((key: keyof IData, value: IData[keyof IData]) => {
    setData((prevData) => ({
      ...prevData,
      [key]: value,
    }))
  }, [])

  return <LayoutContext.Provider value={{ data, updateData: handleUpdate }}>{children}</LayoutContext.Provider>
}