详解 React 中的 Context

什么是 Context

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

Context 的使用场景

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

如何使用 Context

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

createContext

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

export const ImageSizeContext = createContext(500);

SomeContext.Provider

<SomeContext.Provider value={someValue}>
//...
</SomeContext.Provider>
  • 使用 SomeContext.Provider 包裹住一个组件后,其所有的子孙组件都可以通过特定方法获取到指定上下文的值
  • value: 你想通过 context 传递的值,这个值可以时任意类型,如果 value 不传,会往上找 createContext 时定义的初始值。(value 不传时,控制台会报警告)
// ...
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

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
function PlaceImage({ place }) {
  // 🟡 useContext 出来前使用的方法,现在不推荐使用
  return (
    <ImageSizeContext.Consumer>
      {({ imageSize }) => (
          <img
            src={getImageUrl(place)}
            alt={place.name}
            width={imageSize}
            height={imageSize}
          />
        )}
      </ImageSizeContext.Consumer>
  );
}
useContext(SomeContext) - 推荐使用
function PlaceImage({ place }) {
  // ✅ 推荐使用
  const { imageSize } = useContext(ImageSizeContext);
  return (
    <img
      src={getImageUrl(place)}
      alt={place.name}
      width={imageSize}
      height={imageSize}
    />
  );
}

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

// 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}
    />
  );
}

// Contexts.js
import { createContext } from 'react';

export const ImageSizeContext = createContext(500);
// 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'
}];
// 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 和性能优化

  • 12
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值