The below question relates to the following sections in the React Context documentation:
Disclaimer: Apologies for all the background information below. It provides context and will hopefully be helpful to future visitors .
What We Know
themes.dark
(an object that contains two properties: foreground
and background
) Providers
above the Consumer
in the component treeProvider
present in the top-level component ( App
)Provider
( App
), passes down its own state
as the context valueProvider
equal in structure and type to the default context value (avoids Consumers
getting confused)state
in the top-level component ( App
) holds an object of the same format as the default context value : themes.light
Consumer
reads the context , it reads App
's stateApp
) state
deep down in the component tree, without having to pass it through every component in the middleApp
) changes, it re-renders and a new value for state is provided to the Consumer
Consumer
reads the parent's state
, via contextstate
( toggleTheme
) is passed down the component tree as a normal prop
context
only contains an object that reads state
Consumer
by passing the setState
function as a normal prop
from the Provider
's child, down through all the intermediate components, and in to the Consumer
state
in the top-level component ( App
), leads to a re-render of itself, which leads to a re-render of the Provider
, which then passes the new App
state
value down to its Consumer
via contextConsumer
always knows App
's state, via contextstate
is provided as context value to child Consumer
(s)state
is updated by some childProvider
sees that context value ( App
's state
) has changed, and re-renders all its Consumer
s with the new value state
in the Consumer
, by passing the setState
function within the contextprop
to set state
Questions
We know from the docs that:
Every Context object comes with a Provider React component that allows consuming components to subscribe to context changes....
All consumers that are descendants of a Provider will re-render whenever the Provider's value prop changes.
App
as the context value . We know from the above quote that changing it leads to the Provider
re-rendering. Why then, do we bother using state
as the context value? What is the benefit of that, vs. just using any normal variable in App
?state
. Why is link 2 incorporating the function to update state
within state
itself? Could we not just have it as a separate setState
function, which is passed to the Consumer
via context in an object that has two properties (one is state
and the other is the standalone function to update state
)?Let's assume we use a normal variable in App as the context value. We know from the above quote that changing it leads to the Provider re-rendering. Why then, do we bother using state as the context value? What is the benefit of that, vs. just using any normal variable in App?
It's true that when the provider is rerendered with a changed value, any descendents that care about the context will rerender. But you need something to cause the provider to rerender in the first place. This will happen when App's state or its props change (or when you call forceUpdate, but don't do that). Presumably, this is at the top of your application, so there are no props coming in, which means you'll use state to cause it to rerender.
Both the two approaches above allow us to update state. Why is link 2 incorporating the function to update state within state itself? Could we not just have it as a separate setState function, which is passed to the Consumer via context in an object that has two properties (one is state and the other is the standalone function to update state)?
When deciding whether to rerender descendants due to a change of context, react will do basically a ===
between the old value and the new value. This is super quick and works well with React's preference for immutable data, but when using objects as your value you need to be careful that you're not making new objects on every render. For example, if App is doing something like the following, it will be creating a brand new object every time it renders, and thus will be forcing all the context consumers to rerender as well:
class App extends Component {
state = {
data: {
hello: 'world',
}
}
updateData() {
// some function for updating the state
}
render() {
return (
<MyContext.Provider value={{
data: this.state.data,
updateData: this.updateData
}} />
)
}
}
So the example where they store the function in state is to make sure that the entire value they're providing does not change from one render to another.
Let's assume we use a normal variable in App as the context value. We know from the above quote that changing it leads to the Provider re-rendering. Why then, do we bother using state as the context value? What is the benefit of that, vs. just using any normal variable in App?
When you use state and update it - it does not matter at all if you are using provider or not - all the provider and components under it will update. Thats under official React Context documentation and is wrong. It means changing provider values DOES NOT call consumer updates at all.
You can validate this by making a separate component with state (which would not be inside provider) and assign that state variable to the provider. So when component state changes, value in state changes and in turn provider should notice this and update consumer. Which it is NOT doing.
In order to update components under consumers, you, unfortunately, have to do it manually. Unless your intension is to update everything under the provider.
This is true as of 2021-04-21 under React 17.0.2 - provider value changes are not being monitored and consumers are not being updated sadly. Unless you put all your provider in component with state, but changing its state forces updating all components under the provider. Sadly.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.