详解 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 和性能优化

相关推荐
做前端的娜娜子2 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅2 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni2 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学4 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端
先吃饱再说4 小时前
编写一个颜色选择器之后,我理解了 React 工程化的三层架构
react.js·前端框架·前端工程化
先吃饱再说4 小时前
别说你懂 useEffect:从底层机制到生命周期管理,这篇全讲透了
react.js·前端框架
赵庆明老师4 小时前
Vben精讲:21-详解web-antd:tsconfig.json
前端·json·vim
先吃饱再说4 小时前
从“传事件”到“只传值”:React + TypeScript 组件 Props 设计的两次进化
react.js·前端框架·前端工程化
wc884 小时前
微软EDGE浏览器功能学习
前端·学习·edge