logo头像
Snippet 博客主题

React Context实践

参考链接

Context
聊一聊我对 React Context 的理解以及应用
react new context API的一次实践

使用场景

在 React 组件化开发过程中,每个组件会有一些公共属性,比如主题颜色、字体、资源路径等等,有几种方法可以传输这些数据:

  • 在组件树中通过逐层传递 props 或者 state 的方式来传递数据
  • 使用Context来实现跨层级的组件数据传递
  • 使用 Redux 状态管理机进行数据管理

第一种方法显然很不方便,而项目如果不太复杂也没必要使用 Redux,有些公用数据是固定写死的,不会在应用运行过程中改变状态,我们只需在组件中获取该数据,这时候可以考虑使用 Context 实现跨层级的组件通信

基本用法

通过静态方法 React.createContext() 创建一个Context 对象,这个 Context 对象包含两个组件,<Provider /><Consumer />

1
2
3
4
5
6
7
import React from 'react';
import ReactDOM from 'react-dom';

const ThemeContext = React.createContext({
background: 'red',
color: 'white'
});

父组件:

1
2
3
4
5
6
7
8
class App extends React.Component { 
render () {
return (
<ThemeContext.Provider value={{background: 'green', color: 'white'}}>
<Header />
</ThemeContext.Provider> );
}
}

子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Header extends React.Component { 
render () {
return (
<Title>Hello React Context API</Title>
);
}
}
class Title extends React.Component {
render () {
return (
<ThemeContext.Consumer>
{context => (
<h1 style={{background: context.background, color: context.color}}>
{this.props.children}
</h1>
)}
</ThemeContext.Consumer>
);
}
}

<Consumer />children 必须是一个函数,通过函数的参数获取 <Provider /> 提供的Context。

封装 Consumer

context/index.js

1
2
3
4
5
6
7
import * as React from "react";

export const context = {
PIC_URL: 'http://127.0.0.1:8080/img'
};

export default React.createContext(context);

App.js

1
2
3
4
5
6
7
8
9
10
11
12
import Context, { context } from "./context";
class App extends Component {
render() {
return (
<Context.Provider value={context}>
<div> </div>
</Context.Provider>
);
}
}

export default App;

context/consumer.js

1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react';
import Context from "./index";

export const Consumer = Component => {
return props => (
<Context.Consumer>
{context => (
<Component context={context} {...props} />
)}
</Context.Consumer>
);
}

banners.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import React, { Component } from 'react';
import { Consumer } from '../../../../context/Consumer';
import Swiper from 'swiper/dist/js/swiper.js';
import "swiper/dist/css/swiper.css";
import styles from '../../styles.styl';

class Banner extends Component {
. . .

render() {
const { banners } = this.props;
const { PIC_URL } = this.props.context;
const bannerElements = banners.map((banner) => (
<div className="swiper-slide" key={banner.get('id')}>
<a href={banner.get('url')}>
<div className={styles.imgWrapper}>
<h2>{banner.get('title')}</h2>
<img src={`${PIC_URL}?url=${banner.get('image')}`} width="100%" height="100%" />
</div>
</a>
</div>
));
return (
. . .
)
}
}

export default Consumer(Banner);