diff --git a/content/docs/context.md b/content/docs/context.md index 9699bfe3..e98dffad 100644 --- a/content/docs/context.md +++ b/content/docs/context.md @@ -19,6 +19,7 @@ In a typical React application, data is passed top-down (parent to child) via pr - [Consuming Multiple Contexts](#consuming-multiple-contexts) - [Accessing Context in Lifecycle Methods](#accessing-context-in-lifecycle-methods) - [Forwarding Refs to Context Consumers](#forwarding-refs-to-context-consumers) +- [Caveats](#caveats) - [Legacy API](#legacy-api) @@ -68,7 +69,7 @@ Accepts a `value` prop to be passed to Consumers that are descendants of this Pr A React component that subscribes to context changes. -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. All consumers are re-rendered whenever the Provider value changes. 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). +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. All consumers are re-rendered whenever the Provider value changes. 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). (This can cause some issues when passing objects as `value`: see [Caveats](#caveats).) > Note > @@ -89,7 +90,7 @@ A more complex example with dynamic values for the theme: **app.js** `embed:context/theme-detailed-app.js` -## Consuming Multiple Contexts +### Consuming Multiple Contexts To keep context re-rendering fast, React needs to make each context consumer a separate node in the tree. @@ -97,13 +98,13 @@ 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 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` -## Forwarding Refs to Context Consumers +### 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`: @@ -113,6 +114,17 @@ One issue with the render prop API is that refs don't automatically get passed t **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`: + +`embed:context/reference-caveats-problem.js` + + +To get around this, lift the value into the parent's state: + +`embed:context/reference-caveats-solution.js` + ## Legacy API > Note diff --git a/examples/context/reference-caveats-problem.js b/examples/context/reference-caveats-problem.js new file mode 100644 index 00000000..1d1974b4 --- /dev/null +++ b/examples/context/reference-caveats-problem.js @@ -0,0 +1,10 @@ +class App extends React.Component { + render() { + // highlight-range{2} + return ( + + + + ) + } +} diff --git a/examples/context/reference-caveats-solution.js b/examples/context/reference-caveats-solution.js new file mode 100644 index 00000000..c2d12df8 --- /dev/null +++ b/examples/context/reference-caveats-solution.js @@ -0,0 +1,17 @@ +class App extends React.Component { + constructor(props) { + // highlight-range{2} + this.state = { + value: {something: 'something'} + } + } + + render() { + // highlight-range{2} + return ( + + + + ) + } +}