React Context is a way to make an object available in all components of my React tree without having to thread the object through the props of intermediary components. ES6 modules also offer a simple way to make a singleton global object.
Context is a little more cumbersome because I have to add an element to the render tree for every new thing I want to put in the "global scope". So what's the advantage of using Context over a singleton global object?
The answer I settled with, as alluded to by azium's comment:
Context provides more than just a global variable because it can depend on props or state which might change. On change of the context's dependencies, the context would update and anything that's dependent on the context would rerender.
Actually context does not update your components by itself. Context only updates your components if its using state or some kind of observable.
To answer your question. You could use a singleton with an observer pattern like mobx
Related
My understanding is that React's Context API was essentially introduced for quick and dirty global state management, particularly before Redux Toolkit was introduced to simplify the overhead of implementing Redux.
My understanding is that one of the main downsides of Context API is that it any update to any property on it will re-render all component fields which are bound to Context API (full re-render).
I recently explained that downside to someone and he asked why that wouldn't be the case with Redux. He said Redux uses the Context API under the covers which appears to be the case based on some googling:
https://react-redux.js.org/using-react-redux/accessing-store#:~:text=Internally%2C%20React%20Redux%20uses%20React's,object%20instance%20generated%20by%20React.
A few questions:
1 - Can you confirm that Context API does a full component re-render when any state value is changed on it?
2 - Can you confirm that Redux uses Context API under the covers? And can you confirm if Redux Toolkit still uses Context API under the covers?
3 - I was under the impression that Context API was introduced to be a simpler alternative to tools like Redux. But if Redux uses Context API then did Context API come first but maybe it wasn't exposed directly to the developer?
4 - If Redux does use Context API under the covers then can you provide any insight into how Redux avoids a full component re-render? I would assume that there's somevtype of mapping layer but I'd be interested to get some more insight into the specific implementation details
My understanding is that React's Context API was essentially introduced for quick and dirty global state management
That's a common misunderstanding. Context is not a state management system, any more than props is a state management system. Context (like props) is a way to get data from one component to another. The difference is that props always passes the data to direct children, while context makes the data available to whichever random components in a subtree are interested.
My understanding is that one of the main downsides of Context API is that it any update to any property on it will re-render all component fields which are bound to Context API (full re-render).
This is true. Similarly, if you change props, the component that receives those props must rerender
1 - Can you confirm that Context API does a full component re-render when any state value is changed on it?
Of the specific components that are listening to that context, yes.
2 - Can you confirm that Redux uses Context API under the covers? And can you confirm if Redux Toolkit still uses Context API under the covers?
React-redux does use context, yes. Pure redux and redux toolkit don't, since they're general purpose libraries not directly related to react, but I think you meant react-redux.
That <Provider store={store}> that you must render at the top of a react-redux app is there to provide a context to the components underneath it. Components that call hooks like useSelector or useDispatch then use the context to find the store that they should interact with.
3 - I was under the impression that Context API was introduced to be a simpler alternative to tools like Redux. But if Redux uses Context API then did Context API come first but maybe it wasn't exposed directly to the developer?
Context has existed for a long time, but it used to be an unofficial feature. They've also made it easier to use over time.
4 - If Redux does use Context API under the covers then can you provide any insight into how Redux avoids a full component re-render?
The context is only providing a minimal amount of things to the child components, most importantly the store object. The reference to the store object does not typically change, so since the context value does not change, the child components do not need to render. To see exactly what it's providing, see this code: https://github.com/reduxjs/react-redux/blob/master/src/components/Provider.tsx#L33
The contents of the store does change, but that's not what the context is providing. To get the contents of the store, the individual components subscribe to the store, using redux's store.subscribe, plus a special hook called useSyncExternalStore. Basically, redux fires an event when the store's state is updated, and then the individual components set their own local state if it's a change they care about. This local state change is what causes the rerender.
If you're writing code that uses context, you're rarely going to be doing things fancy enough to require useSyncExternalStore or a custom subscription system. So the main things you'll want to keep in mind are:
Keep the context focused on a single task. For example, if you have a theme object to control your app's colors, and also a user object which describes who is currently logged in, put these in different contexts. That way a component that just cares about the theme doesn't need to rerender when the user changes, and vice versa.
If your context value is an object, memoize it so it's not changing on every render (see this documentation)
I'm a Redux maintainer. #NicholasTower gave a great answer, but to give some more details:
Context and Redux are very different tools that solve different problems, with some overlap.
Context is not a "state management" tool. It's a Dependency Injection mechanism, whose only purpose is to make a single value accessible to a nested tree of React components. It's up to you to decide what that value is, and how it's created. Typically, that's done using data from React component state, ie, useState and useReducer. So, you're actually doing all the "state management" yourself - Context just gives you a way to pass it down the tree.
Redux is a library and a pattern for separating your state update logic from the rest of your app, and making it easy to trace when/where/why/how your state has changed. It also gives your whole app the ability to access any piece of state in any component.
In addition, there are some distinct differences between how Context and (React-)Redux pass along updates. Context has some major perf limitations - in particular, any component that consumes a context will be forced to re-render, even if it only cares about part of the context value.
Context is a great tool by itself, and I use it frequently in my own apps. But, Context doesn't "replace Redux". Sure, you can use both of them to pass data down, but they're not the same thing. It's like asking "Can I replace a hammer with a screwdriver?". No, they're different tools, and you use them to solve different problems.
Because this is such a common question, I wrote an extensive post detailing the differences:
Why React Context is Not a "State Management" Tool (and Why It Doesn't Replace Redux)
To answer your questions specifically:
Yes, updating a Context value forces all components consuming that context to re-render... but there's actually a good chance that they would be re-rendering anyway because React renders recursively by default, and setting state in a parent component causes all components inside of that parent to re-render unless you specifically try to avoid it. See my post A (Mostly) Complete Guide to React Rendering Behavior, which explains how all this works.
Yes, React-Redux does use Context internally... but only to pass down the Redux store instance, and not the current state value. This leads to very different update characteristics. Redux Toolkit, on the other hand, is just about the Redux logic and not related to any UI framework specifically.
Context was not introduced to be an alternative to Redux. There was a "legacy Context" API that existed in React well before Redux itself was created, and React-Redux used that up through v5. However, that legacy context API was broken in some key ways. The current React Context API was introduced in React 16.3 to fix the problems in legacy Context, not specifically to replace Redux.
React-Redux uses store subscriptions and selectors in each component instance, which is a completely different mechanism than how Context operates.
I'd definitely suggest reading the posts I linked above, as well as these other related posts:
Redux - Not Dead Yet!
When (and when not) to Reach for Redux
React, Redux, and Context Behavior.
Original blog post by Mark Erikson:
I'll just copy paste some info, but here's the original source and I recommend going directly here: https://blog.isquaredsoftware.com/2020/01/blogged-answers-react-redux-and-context-behavior/
More links here:
https://github.com/markerikson/react-redux-links
https://blog.isquaredsoftware.com/2018/11/react-redux-history-implementation/
https://medium.com/async/how-useselector-can-trigger-an-update-only-when-we-want-it-to-a8d92306f559
An explanation of how React Context behaves, and how React-Redux uses Context internally
There's a couple assumptions that I've seen pop up repeatedly:
React-Redux is "just a wrapper around React context"
You can avoid re-renders caused by React context if you destructure the context value
Both of these assumptions are incorrect, and I want to clarify how they actually work so that you can avoid mis-using them in the future.
For context behavior, say we have this initial setup:
function ProviderComponent() {
const [contextValue, setContextValue] = useState({a: 1, b: 2});
return (
<MyContext.Provider value={contextValue}>
<SomeLargeComponentTree />
</MyContext.Provider>
)
}
function ChildComponent() {
const {a} = useContext(MyContext);
return <div>{a}</div>
}
If the ProviderComponent were to then call setContextValue({a: 1, b: 3}), the ChildComponent would re-render, even though it only cares about the a field based on destructuring. It also doesn't matter how many levels of hooks are wrapping that useContext(MyContext) call. A new reference was passed into the provider, so all consumers will re-render. In fact, if I were to explicitly re-render with <MyContext.Provider value={{a: 1, b: 2}}>, ChildComponent would still re-render because a new object reference has been passed into the provider! (Note that this is why you should never pass object literals directly into context providers, but rather either keep the data in state or memoize the creation of the context value.)
For React-Redux: yes, it uses context internally, but only to pass the Redux store instance down to child components - it doesn't pass the store state using context!. If you look at the actual implementation, it's roughly this but with more complexity:
function useSelector(selector) {
const [, forceRender] = useReducer( counter => counter + 1, 0);
const {store} = useContext(ReactReduxContext);
const selectedValueRef = useRef(selector(store.getState()));
useLayoutEffect(() => {
const unsubscribe = store.subscribe(() => {
const storeState = store.getState();
const latestSelectedValue = selector(storeState);
if(latestSelectedValue !== selectedValueRef.current) {
selectedValueRef.current = latestSelectedValue;
forceRender();
}
})
return unsubscribe;
}, [store])
return selectedValueRef.current;
}
So, React-Redux only uses context to pass the store itself down, and then uses store.subscribe() to be notified when the store state has changed. This results in very different performance behavior than using context to pass data.
There was an extensive discussion of context behavior in React issue #14110: Provide more ways to bail out of hooks. In that thread, Sebastian Markbage specifically said:
My personal summary is that new context is ready to be used for low frequency unlikely updates (like locale/theme). It's also good to use it in the same way as old context was used. I.e. for static values and then propagate updates through subscriptions. It's not ready to be used as a replacement for all Flux-like state propagation.
In fact, we did try to pass the store state in context in React-Redux v6, and it turned out to be insufficiently performant for our needs, which is why we had to rewrite the internal implementation to use direct subscriptions again in React-Redux v7.
For complete detail on how React-Redux actually works, read my post The History and Implementation of React-Redux, which covers the changes to the internal implementation over time, and how we actually use context.
The function of react context is to facilitate the component to pass values, but it is not responsive. It needs to cooperate with usestate to achieve responsiveness. What's the difference between using global variables and setting a layer of usestate can also achieve the effect of component transparent transmission. I can think of the difference between context and global variables is that context only transmits data to wrapped components, while global variables have no such restriction
props and context are the ways that react has for moving data between components. Built into these are the fact that if you rerender the source component and it has a new value for the prop/context, then the destination component will rerender too. The child component does not need to somehow register an event listener on the props/context, and then set state when the value changes; it's enough that the props/context changed.
If you do a global variable, then yes, it can in principle be available to all components. However, you will need to write code which listens to changes in that global variable (for example, with an event emitter or an observable), and then every component that consumes it will need to set local state when the global value changes, in order to rerender itself.
context only transmits data to wrapped components, while global variables have no such restriction
While you describe this as a restriction, it's sometimes a very useful benefit. For example, suppose you have a Theme object that sets the colors for your app. It's provided via context, and your ui components use it to style themselves.
But you also want to let the user change the theme, and see a preview of the change. Since the Theme is using context, you can create a new context provider that just wraps around the preview part of your app. Components inside the preview will use the modified theme, while the rest of the app behaves as normal.
So, i know the difference between the two but after hooks i'm kinda confused about when i should use stateless and when use statefull.
When i was learning the basics of React i was told that stateless is the "dumb" function and statefull is "smart". And that i should use stateless in simple things, because props are imutable and also use more than statefull. I dont really know the difference with Hooks changes in 16.8.
I know that we can use setState now but i dont understand it totally.
This is a great question, and one that I think people will have trouble with for a while. The way I see it, the term "stateless" has been dropped from regular component verbiage and they are just called "functional components".
The concept of "stateless" is still very important though, because it involves no inner state that does not mimic its props. As you said, they are dumb. Generally, if a component can be stateless, it will be more performant. Components that are rendered in loops with a high count do much better with this type of structure. On the other hand, don't stress too much about performance until you're hitting bottlenecks, which shouldn't happen until you've got thousands (or more) of components rendering at once.
To address hooks- they allow you to "hook" into the state and lifecycle of a component. As you will see in the docs, they do not offer more functionality, but a simpler API. This leads to cleaner code and more reusable custom hooks.
If you are dabbling with hooks, maybe try it on some new components you need to build. I've found it to be fun and simplifies my code considerably.
As a final answer, I would say that the concepts of "stateful" and "stateless" components is the same. Only the API that allows you to utilize state has been changed.
Your 'dumb' components should remaing dumb.
Your 'smart' components, can take advantage of hooks to make them 'dumb' with a hint of smart.
Imagine a component where you have to make it 'smart' because there is a toggle in it.
With the old way, you would create a component that has State.
With hooks, you can create a dumb functional component, that just happens to use the hook useToggle.
This keeps your code simple and concise, while at the same time keeping the same functionality you used to have building smart components.
Hooks are just another means to use state (and other functionality) in a so-called "smart", functional, component. That said, their existence doesn't change the answer to this question (of which, there are many).
One example of an appropriate use of state is when you have a component that will render different output based on some sort of change to the component after the initial render. More specifically, if you have a component that needs to make a network call to fetch some data for display, you could use state to keep track of the initial non-existence of that data and update it when the network call returns using setState.
In my experience, as a general pattern, you should use state for things that change and props for things that don't.
I think, the question is actually simple, when do we use the state hook in react? The answer is, if you write a function component and realize you need to add some state to it, now you can use a state hook inside that existing function component. Previously you had to convert it to a class component.
Then why don't we use the class component from the beginning instead of function component? Because when it was first introduced, the recommended pattern for react developers was to use as many stateless components as possible, in other words as many function component.
And in my personal opinion, the function component is neater and easier to use, maybe even more suitable with the reusable component concept. So then, yeah, now we can expand the use of the function component even more.
Hope it helps
I know It may sound like a dumb question, But I am not able to get this solved in my head. Please bear with me.
In case when we use a state management system in React like Redux / Mob X, I guess the main purpose of these state management techniques is to provide a single source of Data and a more structured approach of updating it.
Say, I am Using a state management library(MobX) for React, And suppose I have a parent component which makes an http API call and updates the MobX store with the API response. Now I need that data in one of child/nested components.
My Question is, Should I pass that data as a prop to child component or should I enable child component to connect with Central Store and directly get that data ?
by connecting the child to store, I am turning the Child into a class component, which is making it more heavy and React optimisations may not apply. I am defeating the whole purpose of a functional component.
Awaiting replies.
Best Regards,
Lalit
This completely depends on the situation. I would suggest splitting your components up in 2 parts:
Components that could be re-used in other projects
(Higher level) Components that are so specific to this project that they probably never will be re-used.
For components of category 1, I would suggest not using mobx store directly, but instead make pure react components. (eg think of a dropdown, or an ajax dropdown component).
For second part components (think of, header, footer, section components specific for your website). just make them directly interact with the Mobx store, so that you can way quicker code what you need (instead of constantly having to prop everything).
addition
For components of category 1 you can always wrap them with the #inject() method. This way for example you could turn a dropdown component into a UserDropdown component that uses the mobx store for its state. (The inject method injects mobx state as props in the component).
const UserDropDownComponent = mobx.inject(stores => ({users: stores.userStore.users}))(DropDownComponent);
// usage:
<UserDropDownComponent />
Warning
For pure components wont always see changes of mobx state. To Fix this you need to wrap the component in an #observe annotation. Or you have to inject the props by wrapping it into: mobx.toJS(yourMobxStateProperty)
I have a brand new project using React, Redux, and Immutable. I create my state that is a heavily nested Immutable Map. At what point, if any, should I call toJS on it as I pass it's parts down into the child component's props?
I think it makes sense to keep it always one of the Immutable classes (esp in a new project), and I really like the getIn functionality.
Downside, it's not clear if an object deep inside a presentation component is an Immutable class or a plain JS Object.
I recommend keeping it as an Immutable all the way down.
If you ever call toJS on a piece of your Immutable state prior to passing it as a prop to a child component, you'll lose the benefit of that child component being able to do a simple reference check on that prop in its shouldComponentUpdate. See the advanced performance section of the React docs for more info.
Is there a reason you ever want to call toJS? Ultimately, you're just going to be accessing primitive values for your UI eventually anyway, so it's easy enough to access them with get/getIn. And as a bonus, those Immutable types come with some handy methods.
As for the downside you mention, if you use react-immutable-proptypes it makes it easy to see/enforce what each component expects. And if you make the decision to go Immutable all the way down, there's nothing to remember anyway.