Skip to content

Commit 896214b

Browse files
sebmarkbagegaearon
authored andcommitted
Document class contextType as the primary consuming mechanism (reactjs#1283)
* Document class contextType as the primary consuming mechanism * Update context.md
1 parent 1601307 commit 896214b

10 files changed

+88
-169
lines changed

content/docs/context.md

+65-58
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,13 @@ In a typical React application, data is passed top-down (parent to child) via pr
1212
- [Before You Use Context](#before-you-use-context)
1313
- [API](#api)
1414
- [React.createContext](#reactcreatecontext)
15-
- [Provider](#provider)
16-
- [Consumer](#consumer)
15+
- [Context.Provider](#contextprovider)
16+
- [Class.contextType](#classcontexttype)
17+
- [Context.Consumer](#contextconsumer)
1718
- [Examples](#examples)
1819
- [Dynamic Context](#dynamic-context)
1920
- [Updating Context from a Nested Component](#updating-context-from-a-nested-component)
2021
- [Consuming Multiple Contexts](#consuming-multiple-contexts)
21-
- [Accessing Context in Lifecycle Methods](#accessing-context-in-lifecycle-methods)
22-
- [Consuming Context with a HOC](#consuming-context-with-a-hoc)
23-
- [Forwarding Refs to Context Consumers](#forwarding-refs-to-context-consumers)
2422
- [Caveats](#caveats)
2523
- [Legacy API](#legacy-api)
2624

@@ -114,46 +112,89 @@ However, sometimes the same data needs to be accessible by many components in th
114112
### `React.createContext`
115113

116114
```js
117-
const {Provider, Consumer} = React.createContext(defaultValue);
115+
const MyContext = React.createContext(defaultValue);
118116
```
119117

120-
Creates a `{ Provider, Consumer }` pair. When React renders a context `Consumer`, it will read the current context value from the closest matching `Provider` above it in the tree.
118+
Creates an Context object. When React renders a component that subscribes to this Context object it will read the current context value from the closest matching `Provider` above it in the tree.
121119

122-
The `defaultValue` argument is **only** used by a Consumer when it does not have a matching Provider above it in the tree. This can be helpful for testing components in isolation without wrapping them. Note: passing `undefined` as a Provider value does not cause Consumers to use `defaultValue`.
120+
The `defaultValue` argument is **only** used when a component does not have a matching Provider above it in the tree. This can be helpful for testing components in isolation without wrapping them. Note: passing `undefined` as a Provider value does not cause consuming components to use `defaultValue`.
123121

124-
### `Provider`
122+
### `Context.Provider`
125123

126124
```js
127-
<Provider value={/* some value */}>
125+
<MyContext.Provider value={/* some value */}>
128126
```
129127

130-
A React component that allows Consumers to subscribe to context changes.
128+
Every Context object comes with a Provider React component that allows consuming components to subscribe to context changes.
131129

132-
Accepts a `value` prop to be passed to Consumers that are descendants of this Provider. One Provider can be connected to many Consumers. Providers can be nested to override values deeper within the tree.
130+
Accepts a `value` prop to be passed to consuming components that are descendants of this Provider. One Provider can be connected to many consumers. Providers can be nested to override values deeper within the tree.
133131

134-
### `Consumer`
132+
All consumers that are descendants of a Provider will re-render whenever the Provider's `value` prop changes. The propagation from Provider to its descendant consumers is not subject to the `shouldComponentUpdate` method, so the consumer is updated even when an ancestor component bails out of the update.
133+
134+
Changes are determined by comparing the new and old values using the same algorithm as [`Object.is`](//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description).
135+
136+
> Note
137+
>
138+
> The way changes are determined can cause some issues when passing objects as `value`: see [Caveats](#caveats).
139+
140+
### `Class.contextType`
135141

136142
```js
137-
<Consumer>
138-
{value => /* render something based on the context value */}
139-
</Consumer>
143+
class MyClass extends React.Component {
144+
componentDidMount() {
145+
let value = this.context;
146+
/* perform a side-effect at mount using the value of MyContext */
147+
}
148+
componentDidUpdate() {
149+
let value = this.context;
150+
/* ... */
151+
}
152+
componentWillUnmount() {
153+
let value = this.context;
154+
/* ... */
155+
}
156+
render() {
157+
let value = this.context;
158+
/* render something based on the value of MyContext */
159+
}
160+
}
161+
MyClass.contextType = MyContext;
140162
```
141163

142-
A React component that subscribes to context changes.
164+
The `contextType` property on a class can be assigned a Context object created by [`React.createContext()`](#reactcreatecontext). This lets you consume the nearest current value of that Context type using `this.context`. You can reference this in any of the lifecycle methods including the render function.
143165

144-
Requires a [function as a child](/docs/render-props.html#using-props-other-than-render). The function receives the current context value and returns a React node. The `value` argument passed to the function will be equal to the `value` prop of the closest Provider for this context above in the tree. If there is no Provider for this context above, the `value` argument will be equal to the `defaultValue` that was passed to `createContext()`.
166+
> Note:
167+
>
168+
> You can only subscribe to a single context using this API. If you need to read more than one see [Consuming Multiple Contexts](#consuming-multiple-contexts).
169+
>
170+
> If you are using the experimental [public class fields syntax](https://babeljs.io/docs/plugins/transform-class-properties/), you can use a **static** class field to initialize your `contextType`.
145171
146-
> Note
147-
>
148-
> For more information about the 'function as a child' pattern, see [render props](/docs/render-props.html).
149172

150-
All Consumers that are descendants of a Provider will re-render whenever the Provider's `value` prop changes. The propagation from Provider to its descendant Consumers is not subject to the `shouldComponentUpdate` method, so the Consumer is updated even when an ancestor component bails out of the update.
173+
```js
174+
class MyClass extends React.Component {
175+
static contextType = MyContext;
176+
render() {
177+
let value = this.context;
178+
/* render something based on the value */
179+
}
180+
}
181+
```
151182

152-
Changes are determined by comparing the new and old values using the same algorithm as [`Object.is`](//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description).
183+
### `Context.Consumer`
184+
185+
```js
186+
<MyContext.Consumer>
187+
{value => /* render something based on the context value */}
188+
</MyContext.Consumer>
189+
```
190+
191+
A React component that subscribes to context changes. This lets you subscribe to a context within a [function component](/docs/components-and-props.html#function-and-class-components).
192+
193+
Requires a [function as a child](/docs/render-props.html#using-props-other-than-render). The function receives the current context value and returns a React node. The `value` argument passed to the function will be equal to the `value` prop of the closest Provider for this context above in the tree. If there is no Provider for this context above, the `value` argument will be equal to the `defaultValue` that was passed to `createContext()`.
153194

154195
> Note
155196
>
156-
> The way changes are determined can cause some issues when passing objects as `value`: see [Caveats](#caveats).
197+
> For more information about the 'function as a child' pattern, see [render props](/docs/render-props.html).
157198
158199
## Examples
159200

@@ -191,40 +232,6 @@ To keep context re-rendering fast, React needs to make each context consumer a s
191232

192233
If two or more context values are often used together, you might want to consider creating your own render prop component that provides both.
193234

194-
### Accessing Context in Lifecycle Methods
195-
196-
Accessing values from context in lifecycle methods is a relatively common use case. Instead of adding context to every lifecycle method, you just need to pass it as a prop, and then work with it just like you'd normally work with a prop.
197-
198-
`embed:context/lifecycles.js`
199-
200-
### Consuming Context with a HOC
201-
202-
Some types of contexts are consumed by many components (e.g. theme or localization). It can be tedious to explicitly wrap each dependency with a `<Context.Consumer>` element. A [higher-order component](/docs/higher-order-components.html) can help with this.
203-
204-
For example, a button component might consume a theme context like so:
205-
206-
`embed:context/higher-order-component-before.js`
207-
208-
That's alright for a few components, but what if we wanted to use the theme context in a lot of places?
209-
210-
We could create a higher-order component called `withTheme`:
211-
212-
`embed:context/higher-order-component.js`
213-
214-
Now any component that depends on the theme context can easily subscribe to it using the `withTheme` function we've created:
215-
216-
`embed:context/higher-order-component-usage.js`
217-
218-
### Forwarding Refs to Context Consumers
219-
220-
One issue with the render prop API is that refs don't automatically get passed to wrapped elements. To get around this, use `React.forwardRef`:
221-
222-
**fancy-button.js**
223-
`embed:context/forwarding-refs-fancy-button.js`
224-
225-
**app.js**
226-
`embed:context/forwarding-refs-app.js`
227-
228235
## Caveats
229236

230237
Because context uses reference identity to determine when to re-render, there are some gotchas that could trigger unintentional renders in consumers when a provider's parent re-renders. For example, the code below will re-render all consumers every time the Provider re-renders because a new object is always created for `value`:

examples/context/forwarding-refs-app.js

-11
This file was deleted.

examples/context/forwarding-refs-fancy-button.js

-18
This file was deleted.

examples/context/higher-order-component-before.js

-10
This file was deleted.

examples/context/higher-order-component-usage.js

-5
This file was deleted.

examples/context/higher-order-component.js

-18
This file was deleted.

examples/context/lifecycles.js

-28
This file was deleted.

examples/context/motivation-problem.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ function Toolbar(props) {
1717
);
1818
}
1919

20-
function ThemedButton(props) {
21-
return <Button theme={props.theme} />;
20+
class ThemedButton extends React.Component {
21+
render() {
22+
return <Button theme={this.props.theme} />;
23+
}
2224
}

examples/context/motivation-solution.js

+6-7
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,13 @@ function Toolbar(props) {
2929
);
3030
}
3131

32-
function ThemedButton(props) {
32+
class ThemedButton extends React.Component {
3333
// highlight-range{1-3,6}
34-
// Use a Consumer to read the current theme context.
34+
// Assign a contextType to read the current theme context.
3535
// React will find the closest theme Provider above and use its value.
3636
// In this example, the current theme is "dark".
37-
return (
38-
<ThemeContext.Consumer>
39-
{theme => <Button {...props} theme={theme} />}
40-
</ThemeContext.Consumer>
41-
);
37+
static contextType = ThemeContext;
38+
render() {
39+
return <Button theme={this.context} />;
40+
}
4241
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import {ThemeContext} from './theme-context';
22

3-
function ThemedButton(props) {
4-
// highlight-range{2-9}
5-
return (
6-
<ThemeContext.Consumer>
7-
{theme => (
8-
<button
9-
{...props}
10-
style={{backgroundColor: theme.background}}
11-
/>
12-
)}
13-
</ThemeContext.Consumer>
14-
);
3+
class ThemedButton extends React.Component {
4+
// highlight-range{3,12}
5+
render() {
6+
let props = this.props;
7+
let theme = this.context;
8+
return (
9+
<button
10+
{...props}
11+
style={{backgroundColor: theme.background}}
12+
/>
13+
);
14+
}
1515
}
16+
ThemedButton.contextType = ThemeContext;
1617

1718
export default ThemedButton;

0 commit comments

Comments
 (0)