【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()

猜你喜欢

转载自blog.csdn.net/owo_ovo/article/details/143411695