-
Notifications
You must be signed in to change notification settings - Fork 7.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Document class contextType as the primary consuming mechanism #1265
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -12,15 +12,13 @@ In a typical React application, data is passed top-down (parent to child) via pr | |||||
- [Before You Use Context](#before-you-use-context) | ||||||
- [API](#api) | ||||||
- [React.createContext](#reactcreatecontext) | ||||||
- [Provider](#provider) | ||||||
- [Consumer](#consumer) | ||||||
- [Context.Provider](#contextprovider) | ||||||
- [Class.contextType](#classcontexttype) | ||||||
- [Context.Consumer](#contextconsumer) | ||||||
- [Examples](#examples) | ||||||
- [Dynamic Context](#dynamic-context) | ||||||
- [Updating Context from a Nested Component](#updating-context-from-a-nested-component) | ||||||
- [Consuming Multiple Contexts](#consuming-multiple-contexts) | ||||||
- [Accessing Context in Lifecycle Methods](#accessing-context-in-lifecycle-methods) | ||||||
- [Consuming Context with a HOC](#consuming-context-with-a-hoc) | ||||||
- [Forwarding Refs to Context Consumers](#forwarding-refs-to-context-consumers) | ||||||
- [Caveats](#caveats) | ||||||
- [Legacy API](#legacy-api) | ||||||
|
||||||
|
@@ -114,46 +112,89 @@ However, sometimes the same data needs to be accessible by many components in th | |||||
### `React.createContext` | ||||||
|
||||||
```js | ||||||
const {Provider, Consumer} = React.createContext(defaultValue); | ||||||
const MyContext = React.createContext(defaultValue); | ||||||
``` | ||||||
|
||||||
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. | ||||||
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. | ||||||
|
||||||
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`. | ||||||
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`. | ||||||
|
||||||
### `Provider` | ||||||
### `Context.Provider` | ||||||
|
||||||
```js | ||||||
<Provider value={/* some value */}> | ||||||
<MyContext.Provider value={/* some value */}> | ||||||
``` | ||||||
|
||||||
A React component that allows Consumers to subscribe to context changes. | ||||||
Every Context object comes with a Provider React component that allows consuming components to subscribe to context changes. | ||||||
|
||||||
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. | ||||||
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. | ||||||
|
||||||
### `Consumer` | ||||||
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. | ||||||
|
||||||
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). | ||||||
|
||||||
> Note | ||||||
> | ||||||
> The way changes are determined can cause some issues when passing objects as `value`: see [Caveats](#caveats). | ||||||
|
||||||
### `Class.contextType` | ||||||
|
||||||
```js | ||||||
<Consumer> | ||||||
{value => /* render something based on the context value */} | ||||||
</Consumer> | ||||||
class MyClass extends React.Component { | ||||||
componentDidMount() { | ||||||
let value = this.context; | ||||||
/* perform a side-effect at mount using the value of MyContext */ | ||||||
} | ||||||
componentDidUpdate() { | ||||||
let value = this.context; | ||||||
/* ... */ | ||||||
} | ||||||
componentWillUnmount() { | ||||||
let value = this.context; | ||||||
/* ... */ | ||||||
} | ||||||
render() { | ||||||
let value = this.context; | ||||||
/* render soemthing based on the value of MyContext */ | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it worth showing If not here, we should mention that param/behavior somewhere now that the "Accessing Context in Lifecycle Methods" section is gone. |
||||||
} | ||||||
MyClass.contextType = MyContext; | ||||||
``` | ||||||
|
||||||
A React component that subscribes to context changes. | ||||||
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 life-cycles including the render function. | ||||||
|
||||||
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()`. | ||||||
> Note: | ||||||
> | ||||||
> 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). | ||||||
> | ||||||
> 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`. | ||||||
|
||||||
> Note | ||||||
> | ||||||
> For more information about the 'function as a child' pattern, see [render props](/docs/render-props.html). | ||||||
|
||||||
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. | ||||||
```js | ||||||
class MyClass extends React.Component { | ||||||
static contextType = MyContext; | ||||||
render() { | ||||||
let value = this.context; | ||||||
/* render soemthing based on the value */ | ||||||
} | ||||||
} | ||||||
``` | ||||||
|
||||||
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). | ||||||
### `Context.Consumer` | ||||||
|
||||||
```js | ||||||
<MyContext.Consumer> | ||||||
{value => /* render something based on the context value */} | ||||||
</MyContext.Consumer> | ||||||
``` | ||||||
|
||||||
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). | ||||||
|
||||||
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()`. | ||||||
|
||||||
> Note | ||||||
> | ||||||
> The way changes are determined can cause some issues when passing objects as `value`: see [Caveats](#caveats). | ||||||
> For more information about the 'function as a child' pattern, see [render props](/docs/render-props.html). | ||||||
|
||||||
## Examples | ||||||
|
||||||
|
@@ -191,40 +232,6 @@ To keep context re-rendering fast, React needs to make each context consumer a s | |||||
|
||||||
If two or more context values are often used together, you might want to consider creating your own render prop component that provides both. | ||||||
|
||||||
### Accessing Context in Lifecycle Methods | ||||||
|
||||||
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. | ||||||
|
||||||
`embed:context/lifecycles.js` | ||||||
|
||||||
### Consuming Context with a HOC | ||||||
|
||||||
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. | ||||||
|
||||||
For example, a button component might consume a theme context like so: | ||||||
|
||||||
`embed:context/higher-order-component-before.js` | ||||||
|
||||||
That's alright for a few components, but what if we wanted to use the theme context in a lot of places? | ||||||
|
||||||
We could create a higher-order component called `withTheme`: | ||||||
|
||||||
`embed:context/higher-order-component.js` | ||||||
|
||||||
Now any component that depends on the theme context can easily subscribe to it using the `withTheme` function we've created: | ||||||
|
||||||
`embed:context/higher-order-component-usage.js` | ||||||
|
||||||
### Forwarding Refs to Context Consumers | ||||||
|
||||||
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`: | ||||||
|
||||||
**fancy-button.js** | ||||||
`embed:context/forwarding-refs-fancy-button.js` | ||||||
|
||||||
**app.js** | ||||||
`embed:context/forwarding-refs-app.js` | ||||||
|
||||||
## Caveats | ||||||
|
||||||
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`: | ||||||
|
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,18 @@ | ||
import {ThemeContext} from './theme-context'; | ||
|
||
function ThemedButton(props) { | ||
// highlight-range{2-9} | ||
return ( | ||
<ThemeContext.Consumer> | ||
{theme => ( | ||
<button | ||
{...props} | ||
style={{backgroundColor: theme.background}} | ||
/> | ||
)} | ||
</ThemeContext.Consumer> | ||
); | ||
class ThemedButton extends React.Component { | ||
// highlight-range{3,12} | ||
render() { | ||
let props = this.props; | ||
let theme = this.context; | ||
return ( | ||
<button | ||
{...props} | ||
style={{backgroundColor: theme.background}} | ||
/> | ||
); | ||
} | ||
} | ||
ThemedButton.contextType = ThemeContext; | ||
|
||
export default ThemedButton; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should clarify that context is subject to a component's own
shouldComponentUpdate
method, just not its ancestors. (Seems like this wording could be misleading.)