Is there a reason why PWA Studio is using both Redux and useContext?
Just curious as i've never seen this type of state management pattern before.
I would assume it's better to manage state through one state manager rather than two.
Hi @JordanDDisch. Thank you for your report.
To help us process this issue please make sure that you provided sufficient information.
Please, add a comment to assign the issue: @magento I am working on this
I am guessing legacy. Initially useContext wasn't around but is now adopted for new code with redux slowly getting phased out.
This is a recent PR https://github.com/magento/pwa-studio/pull/2685 which refactors some redux handling state.
@fooman its just very confusing to work with two global state management systems. IMHO it would be more beneficial to just stick with Redux as its better and managing complex application state. Was lost trying to figure out why my cart data was being stored with useContext and checkout state was being stored with Redux.
Thanks I will have a look at that PR.
Agreed. Can't really comment for the pwa-studio team but following some of the developments my guess is that there is not much of redux left with it completely gone soon. So hopefully that removes at least that confusing aspect.
On another note, my current team is using a whole standard version behind the current one, so upgrading probably wont be an option when that happens 馃う
Good observations. 馃槃
At the moment, the Redux store is considered legacy. We still need it for a few bits of orchestration that can't really be refactored at this point, but these days we prefer to use Apollo client's cache for new app state. (Ironically, the Apollo client itself uses Redux to manage its internal state.)
Is there a reason why PWA Studio is using both Redux and useContext?
When we created the project in 2018, React hooks didn't exist, so we started with Redux. More to the point, we started with react-redux and its connect() HOC, as well as redux-thunk for async actions. These libraries have been standard choices for a long time, and we were comfortable with them. Once React introduced hooks, though, we came up with some big plans that involved using hooks everywhere, so we converted all of our components from classes to functions. This left us without a way to bind components to the store, since the connect() HOC is class-based and react-redux didn't have any hooks forthcoming.
Our solution was to pre-slice the store, then provide slices and their actions through a tree of context providers. This way, components can easily access a slice with useContext(), and we can use standard useMemo() and useCallback() hooks in the providers to achieve the same level of memoization that react-redux does for connected components, which is important for minimizing re-renders.
These days, react-redux _does_ offer a hook, useSelector(), and you can use it in your components if you prefer. At some point, we'll probably evaluate updating our context providers to use it internally as well.
I would assume it's better to manage state through one state manager rather than two.
We certainly agree. 馃槄 Which is why we've been steadily moving toward Apollo as the single state manager.
Redux really wasn't designed to be dynamically extensible: reducers work best when they know about every property of a state object and every type of action right up front. It's _technically possible_ to load a reducer on demand and add it to the store dynamically, but it's pretty frustrating to account for that when writing selectors, actions, and reducer cases. We don't want that to be the developer experience.
Fortunately, Apollo's local-only fields give us better APIs for appending new fields to app state on the fly, querying and mutating them, and dealing with the absence of those fields. This approach should work better for both code splitting (performance) and arbitrary extension (user customization). We're pretty happy with it so far.
Once we find a way to orchestrate state changes sequentially (as we do now with async actions), we'll be able to shift the last bits of Redux into Apollo, and we'll all be less confused. 馃檪
@jimbo thanks for that thoughtful response! It was very informative and I learned something new about global state management 馃憤
@jimbo Hi, Im really interested in (Ironically, the Apollo client itself uses Redux to manage its internal state.) where can I find this in apollo-clien core ? Cause now I cant see redux there
Oops looks like they removed it
Most helpful comment
Good observations. 馃槃
At the moment, the Redux store is considered legacy. We still need it for a few bits of orchestration that can't really be refactored at this point, but these days we prefer to use Apollo client's cache for new app state. (Ironically, the Apollo client itself uses Redux to manage its internal state.)
When we created the project in 2018, React hooks didn't exist, so we started with Redux. More to the point, we started with
react-reduxand itsconnect()HOC, as well asredux-thunkfor async actions. These libraries have been standard choices for a long time, and we were comfortable with them. Once React introduced hooks, though, we came up with some big plans that involved using hooks everywhere, so we converted all of our components from classes to functions. This left us without a way to bind components to the store, since theconnect()HOC is class-based andreact-reduxdidn't have any hooks forthcoming.Our solution was to pre-slice the store, then provide slices and their actions through a tree of context providers. This way, components can easily access a slice with
useContext(), and we can use standarduseMemo()anduseCallback()hooks in the providers to achieve the same level of memoization thatreact-reduxdoes for connected components, which is important for minimizing re-renders.These days,
react-redux_does_ offer a hook,useSelector(), and you can use it in your components if you prefer. At some point, we'll probably evaluate updating our context providers to use it internally as well.We certainly agree. 馃槄 Which is why we've been steadily moving toward Apollo as the single state manager.
Redux really wasn't designed to be dynamically extensible: reducers work best when they know about every property of a state object and every type of action right up front. It's _technically possible_ to load a reducer on demand and add it to the store dynamically, but it's pretty frustrating to account for that when writing selectors, actions, and reducer cases. We don't want that to be the developer experience.
Fortunately, Apollo's local-only fields give us better APIs for appending new fields to app state on the fly, querying and mutating them, and dealing with the absence of those fields. This approach should work better for both code splitting (performance) and arbitrary extension (user customization). We're pretty happy with it so far.
Once we find a way to orchestrate state changes sequentially (as we do now with async actions), we'll be able to shift the last bits of Redux into Apollo, and we'll all be less confused. 馃檪