React Hooks-useContext的使用

2,019 阅读3分钟

全局共享数据,其他任意组件都能直接使用该数据

优点:无需将该数据一步一步传参到目标组件

# useContext的基本用法

# React中Context的使用方法

一、context 的初始化

根组件(父组件)导入并调用createContext方法,得到Context对象,并导出

import { createContext } from 'react'
export const Context = createContext()

二、provider包裹需要context的子组件

在根组件中使用 Provider 组件包裹需要接收数据的后代组件,并通过 value 属性提供要共享的数据

return (
  <Context.Provider value={ 这里放要传递的数据 }>
  	//需要使用共享数据的后代组件
        <Child1 />
        <Child2 />
  </Provider>
)

三、子组件获得context

需要获取公共数据的后代组件:导入useContext,并按需导入根组件中导出的Context对象; 调用useContext(第一步中导出的Context) 得到value的值

import React, { useContext } from 'react'
import { Context } from './index'
const ChildComponent = () => {
    const 公共数据 = useContext(Context) // 这里的公共数据就是根组件value的值
    return ( 子组件的内容 )
}

举例:组件Child2中的数据,共享到全局,供其他组件直接使用(如Child1)

import * as React from 'react'
// 1、得到 Context对象
const CounterContext = React.createContext()

const FatherComponent = ({children}) => {
  const [count, setCount] = React.useState(0)
  const value = [count, setCount]
  return (
  // 2、provider包裹需要context的子组件,通过value属性 暴露(要全局共享的)数据
    <CounterContext.Provider value={value}>
      {children}
    </CounterContext.Provider>
  )
}

//自定义函数:忘记使用Provider时,拿不到共享数据,因此做错误处理
function getContext() {
  const value = React.useContext(CounterContext)
  if (!value) {
    throw new Error('useCountContext must used within CountProvider')
  }
  return value
}

function Child1() {
// 4、组件Child1通过调用自定义函数其中的 useContext 拿到(已暴露)的共享数据
  const [count] = getContext()
//使用该共享数据
  return <div>{`The current count is ${count}`}</div>
}

// 3、调用自定义函数,通过其中的 useContext将数据共享到全局
function Child2() {
  const [count, setCount] = getContext()
  const increment = () => {
    setCount(count + 1)
  }
  return <button onClick={increment}>Increment count</button>
}

function App() {
  return (
    <div>
      <FatherComponent>
        <Child1 />
        <Child2 />
      </FatherComponent>
    </div>
  )
}

export default App

举例:将父组件中的数据value全局共享,在子组件Child1中使用

import React, { useContext } from 'react'

const MyContext = React.createContext()
const FatherComponent = () => {
  const [value, setValue] = useState('initValue')
  return (
    <div>
      <button onClick={() => {
        setValue('newValue')
      }}>
        改变value
      </button>
      <MyContext.Provider value={value}>
        <Child1 />
        <Child2 />
      </MyContext.Provider>
    </div>
  );
}

const Child1 = () => {
  const value = useContext(MyContext)
  //使用该共享数据
  return <div className={`${classPrefix}-text`}>Child1-value: {value}</div>
}

//未使用共享数据的子组件,使用memo降低性能损耗
const Child2 = memo(() => {
  return <div className={`${classPrefix}-text`}>Child2</div>;
})

useContext会在context值变化时重新渲染,<MyContext.Provider>的value发生变化时,包裹着的子组件无论是否使用value值,都会重新渲染。

可以使用memo对未使用value的子组件进行优化,在组件更新的时候memo会看自身包裹的组件是否有数据更新,如果没有,就会阻止自身组件的重新渲染,减少性能损耗。

举例:组件Child1中的数据,传给父组件,由父组件用Provider共享到全局,供其他组件直接使用(如Child2)

每组类型限制的对应情况使用大写字母进行了标注

父组件:

src/renderer/App.tsx

import React, { useState } from 'react';
import { EnvContextProvider } from '@/hooks/useEnvContext';
import {
  MemoryRouter as Router,
  Switch,
  Route,
  useHistory,
} from 'react-router-dom';

export default function App() {
    ...
    let [dataToBeShared, setDataToBeShared] = useState('1');
    let [arrayToBeShared, setArrayToBeShared] = useState([] as any[]);
    ...
    return (
    // 3、在父组件中使用 Provider组件包裹需要接收(使用)共享数据的后代组件,并通过value属性提供要共享的数据
     <EnvContextProvider
       value={{
        // B.
        organId: dataToBeShared,
        arrayList: arrayToBeShared,
        }}
     >
       <Router
          initialEntries={[initPath]}
       >
         <Switch>
            <Route path="/login" component={Login} />
            <Route
              path="/child1"
              render={() => {
                return (
                  // 2、父组件通过定义函数来接收从子组件Child1传来的参数
                  <Child1
                  //定义函数onAuth其形参是个对象
                    onAuth={({
                    // A.
                      organId,
                      arrayList,
                    }) => {
                    //Child1通过该函数,将数据传给父组件并赋给 dataToBeShared、arrayToBeShared
                      setDataToBeShared(organId);
                      setArrayToBeShared(arrayList);
                    }}
                  ></Child1>
                );
              }}
            />
         </Switch>
       </Router>
     </EnvContextProvider>
    )
}

其它:

src/hooks/useEnvContext/index.ts

export * from "./EnvContextProvider";
export * from "./useEnvContext";

src/hooks/useEnvContext/useEnvContext.ts

import { useContext } from 'react';
import { EnvContext } from './EnvContextProvider';

export const useEnvContext = () => {
  const envContext = useContext(EnvContext);

  return envContext;
};

限制 EnvContextProvider的value的数据类型: src/hooks/useEnvContext/EnvContextProvider.ts

import React from 'react';
import defaultValue from './defaultValue';


export type EnvContextInstance = ReturnType<typeof defaultValue>;

export const EnvContext = React.createContext<EnvContextInstance>(null!);

export const EnvContextProvider = EnvContext.Provider;

export default EnvContext.Provider;

src/hooks/useEnvContext/defaultValue.ts

export default function () {
  return {
    // B.
    organId: '',
    arrayList: [] as any[],
  };
}

子组件Child1:

src/renderer/pages/Child1/index.tsx

import React, { useState } from 'react';
import { BindType } from './types';

//定义传入本组件的 props类型
const Child1: React.FC<{
//从 props中解构出 onAuth函数,限制其参数类型
  onAuth: (params: {
  // A.
    organId: string;
    arrayList: any[];
  }) => void;
}> = function ({ onAuth }) {
  //直接从props中解构出了onAuth,所以在此子组件内部调用该函数将数据传给父组件,无需使用 props.onAuth()

    let isBound = useService<string>({
    serviceId: '',
    serviceMethod: '',
    lazy: true,
    });

  React.useEffect(() => {
    isBound.request(['传给后端接口的参数']).then((res: any) => {
    //从 res 中解构出 enabled,merList
      let {
        enabled,
        merList,
      } = res as BindType;
      if (enabled == 0) {
      //无需使用 props.onAuth()
      // 1、通过该函数将数据传给父组件
        onAuth({
          organId: merList[0].organId,
          arrayList: merList,
        });
      }
    });
  }, []);
}

export default Child1;

不解构出onAuth:

interface IProps {
  onAuth: (params: {
    organId: string;
    arrayList: any[];
  }) => void;
}
const Child1: React.FC<IProps> = function (props) {
    ...
    props.onAuth({
        organId: merList[0].organId,
        arrayList: merList,
    });
    ...
}

src/renderer/pages/Child1/types.ts

定义res中解构出的数据的类型

type MerList = {
  organId: string;
};

export type BindType = {
  enabled: number;
  merList: MerList[];
};

子组件Child2:

src/renderer/pages/Child2/index.tsx

import React from 'react';
import { useEnvContext } from '@/hooks/useEnvContext';

const Child2: React.FC = () => {
// 4、使用该共享数据
  let { organId } = useEnvContext();
  let { arrayList } = useEnvContext();
  let arrayListLength = arrayList.length;
  
  return (
  ...
  )
}
export default Child2;