【React】createContext 和 useContext

createContext

createContext 创建一个上下文对象,可以用于在组件树中共享数据,而不必通过 props 手动传递。

createContext() 会返回一个上下文对象,其中包含两个重要属性:

  • Provider: 用于提供上下文的组件。
  • Consumer: 用于消费上下文的组件。

Provider 提供数据

javascript 复制代码
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 };

通常放在顶级组件

javascript 复制代码
<MyProvider>
    <App />
</MyProvider>

Consumer 使用数据

javascript 复制代码
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,可以直接从上下文对象中解构出 数据。

javascript 复制代码
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>
    );
};

应用场景

如实时监听窗口大小

javascript 复制代码
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

javascript 复制代码
createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <MyProvider>
      <App />
    </MyProvider>
  </StrictMode>
)

使用

javascript 复制代码
const { width, height, isLarge } = useSize()
相关推荐
VillanelleS11 分钟前
Vue2进阶之Vue3高级用法
前端·javascript·vue.js
天农学子15 分钟前
EasyUI弹出框行编辑,通过下拉框实现内容联动
前端·javascript·easyui
格瑞@_@44 分钟前
11.Three.js使用indexeddb前端缓存模型优化前端加载效率
前端·javascript·缓存·three.js·indexeddb缓存
木子七1 小时前
Js Dom
前端·javascript
写bug写bug1 小时前
Git 中的撤销工作区、暂存区和已提交的更改
前端·git·后端
重生之我是菜鸡程序员1 小时前
uniapp 使用vue/pwa
javascript·vue.js·uni-app
谢小飞2 小时前
我做了三把椅子原来纹理这样加载切换
前端·three.js
圈圈的熊2 小时前
HTTP 和 HTTPS 的区别
前端·网络协议·http·https
GIS程序媛—椰子2 小时前
【Vue 全家桶】2、Vue 组件化编程
前端·javascript·vue.js