【React】createContext 和 useContext
createContext
createContext
创建一个上下文对象,可以用于在组件树中共享数据,而不必通过 props 手动传递。
createContext()
会返回一个上下文对象,其中包含两个重要属性:
- Provider: 用于提供上下文的组件。
- Consumer: 用于消费上下文的组件。
Provider 提供数据
import React, { createContext, useState } from 'react';
// 创建一个 Context上下文对象
const MyContext = createContext();
const MyProvider = ({ children }) => {
const [value, setValue] = useState('Hello World');
return (
<MyContext.Provider value={{ value, setValue }}>
{children}
</MyContext.Provider>
);
};
export { MyProvider, MyContext };
通常放在顶级组件
<MyProvider>
<App />
</MyProvider>
Consumer 使用数据
const MyComponentUsingConsumer = () => {
return (
<MyContext.Consumer>
{({ value, setValue }) => (
<div>
<p>{value}</p>
<button onClick={() => setValue('New Value from Consumer')}>
Change Value
</button>
</div>
)}
</MyContext.Consumer>
);
};
useContext
获取上下文对象,可以理解为简化版的 Consumer,可以直接从上下文对象中解构出 数据。
import { useContext } from 'react';
const MyComponentUsingHook = () => {
const { value, setValue } = useContext(MyContext);
return (
<div>
<p>{value}</p>
<button onClick={() => setValue('New Value from useContext')}>
Change Value
</button>
</div>
);
};
应用场景
如实时监听窗口大小
import React, {
createContext,
useContext,
useEffect,
useState,
ReactNode,
} from "react"
// 定义上下文的类型
interface SizeData {
width: number
height: number
isLarge: boolean
}
// 创建上下文并设置默认值为 SizeData 类型
const MyContext = createContext<SizeData | undefined>(undefined)
interface MyProviderProps {
children: ReactNode
}
// 创建 Provider 组件
export const MyProvider: React.FC<MyProviderProps> = ({ children }) => {
const isLarge = () => window.innerWidth > 1024
const [sizeData, setSizeData] = useState<SizeData>({
width: window.innerWidth,
height: window.innerHeight,
isLarge: isLarge(),
})
useEffect(() => {
let timer: number
const handler = () => {
clearTimeout(timer)
timer = setTimeout(() => {
setSizeData({
width: window.innerWidth,
height: window.innerHeight,
isLarge: isLarge(),
})
}, 200)
}
window.addEventListener("resize", handler)
return () => {
window.removeEventListener("resize", handler)
clearTimeout(timer)
}
}, [])
return <MyContext.Provider value={sizeData}>{children}</MyContext.Provider>
}
// 创建自定义 Hook
export const useSize = (): SizeData => {
const context = useContext(MyContext)
// 处理 context 为 undefined 的情况
if (!context) {
throw new Error("useSize must be used within a MyProvider")
}
return context
}
main.tsx
createRoot(document.getElementById("root")!).render(
<StrictMode>
<MyProvider>
<App />
</MyProvider>
</StrictMode>
)
使用
const { width, height, isLarge } = useSize()