详解 React 中的 Context

什么是 Context

context 是 React 提供的一个跨层级通信的方式,它允许父组件向其下面整个树提供数据,让我们可以不用显式地通过组件树逐层传递 props。

Context 的使用场景

  • 主题
  • 当前账户
  • 路由
  • 还有一些状态会被远程组件用到或修改,结合 reducer 和 context 来使用

如何使用 Context

createContext(defaultValue) 创建上下文 SomeContext.Provider 向组件提供上下文值 useContext 函数组件中读取上下文值的方法 SomeContext.Consumer 函数组件中读取上下文值的方法(传统方法) static contextType 类组件中指定上下文的方法,然后通过 this.content 来读取上下文值

createContext

scss 复制代码
createContext(defaultValue)
  • defaultValue: 任何你想通过上下文来传递的有意义的值,如果是没有意义的默认值,使用 null 作为默认值。
  • 返回一个 context object
javascript 复制代码
// Contexts.js
import { createContext } from 'react';

export const ImageSizeContext = createContext(500);

SomeContext.Provider

xml 复制代码
<SomeContext.Provider value={someValue}>
//...
</SomeContext.Provider>
  • 使用 SomeContext.Provider 包裹住一个组件后,其所有的子孙组件都可以通过特定方法获取到指定上下文的值
  • value: 你想通过 context 传递的值,这个值可以时任意类型,如果 value 不传,会往上找 createContext 时定义的初始值。(value 不传时,控制台会报警告)
javascript 复制代码
// ...
export default function App() {
  const [isLarge, setIsLarge] = useState(false);
  const imageSize = isLarge ? 150 : 100;
  return (
    <ImageSizeContext.Provider
      value={{ imageSize }}
    >
      <label>
        <input
          type="checkbox"
          checked={isLarge}
          onChange={e => {
            setIsLarge(e.target.checked);
          }}
        />
        Use large images
      </label>
      <hr />
      <List />
    </ImageSizeContext.Provider>
  )
}

类组件中读取 context 的方法:static contextType

scala 复制代码
class PlaceImage extends Component{
  static contextType = ImageSizeContext
  render() {
    const { place } = this.props
    const { imageSize } = this.context
    return (
        <img
          src={getImageUrl(place)}
          alt={place.name}
          width={imageSize}
          height={imageSize}
        />
    )
  }
}

static childContextTypes 和 static contextTypes 均不建议使用,他们可能会在 react 的某个版本被移除
在类组件中读取 this.context 相当于在函数组件中读取 useContext 。

函数组件中读取 context 的方法:SomeContext.Consumer 和 useContext

SomeContext.Consumer

javascript 复制代码
function PlaceImage({ place }) {
  // 🟡 useContext 出来前使用的方法,现在不推荐使用
  return (
    <ImageSizeContext.Consumer>
      {({ imageSize }) => (
          <img
            src={getImageUrl(place)}
            alt={place.name}
            width={imageSize}
            height={imageSize}
          />
        )}
      </ImageSizeContext.Consumer>
  );
}

useContext(SomeContext)

ini 复制代码
function PlaceImage({ place }) {
  const { imageSize } = useContext(ImageSizeContext);
  return (
    <img
      src={getImageUrl(place)}
      alt={place.name}
      width={imageSize}
      height={imageSize}
    />
  );
}

一个使用 useContext 的完整案例(来源于 react 官网

javascript 复制代码
// App.js
import { useState, useContext } from 'react';
import { places } from './data.js';
import { getImageUrl } from './utils.js';
import { ImageSizeContext } from './Contexts.js';

export default function App() {
  const [isLarge, setIsLarge] = useState(false);
  const imageSize = isLarge ? 150 : 100;
  return (
    <ImageSizeContext.Provider
      value={{ imageSize }}
    >
      <label>
        <input
          type="checkbox"
          checked={isLarge}
          onChange={e => {
            setIsLarge(e.target.checked);
          }}
        />
        Use large images
      </label>
      <hr />
      <List />
    </ImageSizeContext.Provider>
  )
}

function List() {
  const listItems = places.map(place =>
    <li key={place.id}>
      <Place place={place} />
    </li>
  );
  return <ul>{listItems}</ul>;
}

function Place({ place }) {
  return (
    <>
      <PlaceImage place={place} />
      <p>
        <b>{place.name}</b>
        {': ' + place.description}
      </p>
    </>
  );
}

function PlaceImage({ place }) {
  const { imageSize } = useContext(ImageSizeContext);
  return (
    <img
      src={getImageUrl(place)}
      alt={place.name}
      width={imageSize}
      height={imageSize}
    />
  );
}
javascript 复制代码
// Contexts.js
import { createContext } from 'react';

export const ImageSizeContext = createContext(500);
vbnet 复制代码
// data.js
export const places = [{
  id: 0,
  name: 'Bo-Kaap in Cape Town, South Africa',
  description: 'The tradition of choosing bright colors for houses began in the late 20th century.',
  imageId: 'K9HVAGH'
}, {
  id: 1, 
  name: 'Rainbow Village in Taichung, Taiwan',
  description: 'To save the houses from demolition, Huang Yung-Fu, a local resident, painted all 1,200 of them in 1924.',
  imageId: '9EAYZrt'
}, {
  id: 2, 
  name: 'Macromural de Pachuca, Mexico',
  description: 'One of the largest murals in the world covering homes in a hillside neighborhood.',
  imageId: 'DgXHVwu'
}, {
  id: 3, 
  name: 'Selarón Staircase in Rio de Janeiro, Brazil',
  description: 'This landmark was created by Jorge Selarón, a Chilean-born artist, as a "tribute to the Brazilian people".',
  imageId: 'aeO3rpI'
}, {
  id: 4, 
  name: 'Burano, Italy',
  description: 'The houses are painted following a specific color system dating back to 16th century.',
  imageId: 'kxsph5C'
}, {
  id: 5, 
  name: 'Chefchaouen, Marocco',
  description: 'There are a few theories on why the houses are painted blue, including that the color repels mosquitos or that it symbolizes sky and heaven.',
  imageId: 'rTqKo46'
}, {
  id: 6,
  name: 'Gamcheon Culture Village in Busan, South Korea',
  description: 'In 2009, the village was converted into a cultural hub by painting the houses and featuring exhibitions and art installations.',
  imageId: 'ZfQOOzf'
}];
javascript 复制代码
// utils.js
export function getImageUrl(place) {
  return (
    'https://i.imgur.com/' +
    place.imageId +
    'l.jpg'
  );
}

注意事项

  • useContext 作为一个 hook,不能在循环、条件或 map() 调用中调用。
  • 不要过度使用 context,因为 context 值有改动时,其父组件以及整棵树都会重新渲染。所以使用 context 前,先尝试通过 props 传递数据,或者把 JSX 作为 props 来传递

参考文档:
Context
createContext
useContext
Reducer 和 Context
static contextType
context 和性能优化

相关推荐
程序员agions1 小时前
2026年,“配置工程师“终于死绝了
前端·程序人生
alice--小文子1 小时前
cursor-mcp工具使用
java·服务器·前端
晚霞的不甘1 小时前
揭秘 CANN 内存管理:如何让大模型在小设备上“轻装上阵”?
前端·数据库·经验分享·flutter·3d
小迷糊的学习记录1 小时前
0.1 + 0.2 不等于 0.3
前端·javascript·面试
梦帮科技2 小时前
Node.js配置生成器CLI工具开发实战
前端·人工智能·windows·前端框架·node.js·json
VT.馒头2 小时前
【力扣】2695. 包装数组
前端·javascript·算法·leetcode·职场和发展·typescript
css趣多多3 小时前
一个UI内置组件el-scrollbar
前端·javascript·vue.js
C澒3 小时前
前端整洁架构(Clean Architecture)实战解析:从理论到 Todo 项目落地
前端·架构·系统架构·前端框架
C澒3 小时前
Remesh 框架详解:基于 CQRS 的前端领域驱动设计方案
前端·架构·前端框架·状态模式
Charlie_lll3 小时前
学习Three.js–雪花
前端·three.js