全局共享数据,其他任意组件都能直接使用该数据
优点:无需将该数据一步一步传参到目标组件
一、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;