什么是 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 和性能优化