React: React Context Providers cause consumers to re-render even when value hasn't changed

Created on 23 May 2018  路  6Comments  路  Source: facebook/react

Do you want to request a feature or report a bug?

Bug.

What is the current behavior?

The documentation of React Context states that

All Consumers that are descendants of a Provider will re-render whenever the
Provider鈥檚 value prop changes... Changes are determined by comparing the new
and old values using the same algorithm as Object.is.

The example shows that that is not the case. A provider is re-rendered with the
same identical value, and as indicated by the counter, the consumer re-renders
every time the provider is rendered, regardless of whether the value has
changed.

Am I misunderstanding the docs? If this is the intended behavior, how can
context be used efficiently?

https://jsfiddle.net/g8y4hcbj/1/

What is the expected behavior?

Would only indicate the one initial render. Subsequent renders would not cause
consumers of Provider1 to re-render because its value hasn't changed. This is
the case for all types of values (scalar, object, function, etc.).

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?

Observed in 16.3.0, 16.3.1, and 16.3.2.

Most helpful comment

Providers won't do anything special to block updates. Think of them as normal React components. If you have a React component tree, it re-renders completely when you render it due to a setState.

If you want to block updates you can do it yourself. Again, this is not specific to context. You have the same tools at your disposal as always: shouldComponentUpdate and/or PureComponent.

The context update will propagate through those when the value changes. But it will not do anything to prevent a tree from re-rendering when there is a setState above, just like it happens normally in React.

All 6 comments

Providers won't do anything special to block updates. Think of them as normal React components. If you have a React component tree, it re-renders completely when you render it due to a setState.

If you want to block updates you can do it yourself. Again, this is not specific to context. You have the same tools at your disposal as always: shouldComponentUpdate and/or PureComponent.

The context update will propagate through those when the value changes. But it will not do anything to prevent a tree from re-rendering when there is a setState above, just like it happens normally in React.

@gaearon, thanks for the very quick reply! With the help of this clarification, we've been able to get the desired behavior.

鈽笍

Providers won't do anything special to _block_ updates. Think of them as normal React components. If you have a React component tree, it re-renders completely when you render it due to a setState.

If you want to _block_ updates you can do it yourself. Again, this is not specific to context. You have the same tools at your disposal as always: shouldComponentUpdate and/or PureComponent.

The context update will propagate _through_ those when the value changes. But it will not do anything to prevent a tree from re-rendering when there is a setState above, just like it happens normally in React.

@gaearon Would this: https://stackoverflow.com/a/56926538 be a proper solution to block updates in functional components? In the following example i'm only passing a method so I don't want consumers to re-render each time it's called: https://jsfiddle.net/La8p5k76/11/

shahn

can I update the value of the Provider from Context like

consider I have
consnt MyContext = React.createContext({})

and

.........

now I have value is 5
can I update it from MyContext directly?
like
MyContext.Provider.value = 6

I am aware that I can update via Consumer by callback but is there any alternative?

Avoiding unnecessary renders with React context

I got it. Use state to set value will not cause consumers to re-render.

In my case.

const GlobalLayout: React.FC<any> = props => {
  const [config] = useState({
    uploadFn,
  });
  return <FormConfigProvider value={config} {...props} />;
};
Was this page helpful?
0 / 5 - 0 ratings