From 691cd4577cdfaffb64caef2a39af6d4a1739799d Mon Sep 17 00:00:00 2001 From: Brian Vaughn <brian.david.vaughn@gmail.com> Date: Thu, 29 Mar 2018 09:15:30 -0700 Subject: [PATCH] Added inline child function caveat to Context docs --- content/docs/context.md | 14 +++++++++++++- .../consumer-cached-function-as-a-child.js | 15 +++++++++++++++ .../consumer-inline-function-as-a-child.js | 15 +++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 examples/context/consumer-cached-function-as-a-child.js create mode 100644 examples/context/consumer-inline-function-as-a-child.js diff --git a/content/docs/context.md b/content/docs/context.md index 2e54d512..d89f18fe 100644 --- a/content/docs/context.md +++ b/content/docs/context.md @@ -69,7 +69,9 @@ 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). (This can cause some issues when passing objects as `value`: see [Caveats](#caveats).) +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 function should be created inline, as shown in the example above, rather than being bound to the instance or otherwise cached. (Refer to the see [Caveats](#consumer-inline-function-as-a-child) section below for more.) + +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`](https://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](#objectis).) > Note > @@ -134,6 +136,16 @@ One issue with the render prop API is that refs don't automatically get passed t ## Caveats +### `Consumer` Inline Function as a Child + +As an optimization to avoid doing unnecessary work, React currently stops rendering when it sees children that are equal to the most recently rendered children. This applies to the [function as a child](/docs/render-props.html#using-props-other-than-render) pattern used by `Consumer` as well which can cause unexpected behavior in some situations: +`embed:context/consumer-cached-function-as-a-child.js` + +To avoid this scenario, we recommend declaring the child function inline: +`embed:context/consumer-inline-function-as-a-child.js` + +### `Object.is` + 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` diff --git a/examples/context/consumer-cached-function-as-a-child.js b/examples/context/consumer-cached-function-as-a-child.js new file mode 100644 index 00000000..b739e80b --- /dev/null +++ b/examples/context/consumer-cached-function-as-a-child.js @@ -0,0 +1,15 @@ +class Example extends React.Component { + // This method will be called when the context value changes, + // But not when the props value changes! + renderValue = value => { + return ( + <div> + Context value:{value}. Props value:{this.props.counter} + </div> + ); + }; + + render() { + return <Ctx.Consumer>{this.renderValue}</Ctx.Consumer>; + } +} diff --git a/examples/context/consumer-inline-function-as-a-child.js b/examples/context/consumer-inline-function-as-a-child.js new file mode 100644 index 00000000..3b75a54d --- /dev/null +++ b/examples/context/consumer-inline-function-as-a-child.js @@ -0,0 +1,15 @@ +class Example extends React.Component { + render() { + // The inline function will be called when the context value changes, + // And when the props value changes! + return ( + <Ctx.Consumer> + {value => ( + <div> + Context value:{value}. Props value:{this.props.counter} + </div> + )} + </Ctx.Consumer> + ); + } +}