Approach for using nested stores in a decoupled application
See original GitHub issueHey guys,
I have a complex use-case in mind and I’d like to pick your brain for correctly approaching this. Before explaining the problem, I know there have been related questions and I know there is no plan on supporting this, but hear me out.
Overview
Imagine I have an application with some main routes
<Route path="/" component={Application}>
<Route path="dashboard" component={Dashboard} />
<Route path="posts" component={PostsList} />
<Route path="account" component={Account} />
</Route>
We can consider the Application
container as simply the scaffolding component, which might include a TopNavigation
and a LeftNavigation
, and renders the child route as the main
view.
So each main route is basically rendered as the main view.
That’s pretty standard.
The Application
also has a “global” state, which might include user
information, token
for authentication and other stuff necessary for the overall functionality of the app. Of course this includes the related reducers
, actions
and so on.
The root component of the app also is wrapped around a Provider
, like it’s supposed to be.
The idea
Now image that all those main route components (Dashboard
, PostsList
, …) are considered as modules (or plugins, or extensions, whatever name you like) and are basically separated from the main application.
Imagine a concept similar to babel
or eslint
, where you have the core and plugins that can be developed outside.
The idea is that I can develop my Dashboard
module indipendently from the main application. For simplicity let’s say that each module simply exports its routes.
// dashboard
const DashboardRoot = props => (<h1>{'Dashboard'}</h1>)
const DashboardRoute = () => (
<Route path="dashboard" component={DashboardRoot} />
)
export default DashboardRoute
The problem
Here some facts:
- each module can have its own local store with reducers, actions and so on
- modules are decoupled from each other
- the main application doesn’t know about what’s going on in each module
- modules can use and access the “global” state of the application (e.g.
user
)
Given that, I think it’s fair to assume that I would use, in each module, an own Provider
.
const DashboardRoot = props => (
<Provider store={store}>
<h1>{'Dashboard'}</h1>
</Provider>
)
const DashboardRoute = () => (
<Route path="dashboard" component={DashboardRoot} />
)
export default DashboardRoute
At this point there is a problem though. A store
already exists in the context
as it was defined by the root Provider
of the main app.
As suggested in previous questions regarding this topic (nested providers), passing manually the
store
as a prop is not an option, as e.g. I want to use all the goodnesses ofconnect
.
A possible solution
Ideally speaking, I think we could replace the Provider
in each module with some sort of local provider for each module. Let’s call this LocalProvider
.
const DashboardRoot = props => (
<LocalProvider localStore={localStore}>
<h1>{'Dashboard'}</h1>
</LocalProvider>
)
const DashboardRoute = () => (
<Route path="dashboard" component={DashboardRoot} />
)
export default DashboardRoute
Now there are 2 different stores in context
:
- the global application
store
provided by the main app - the local module
localStore
provided in each module
So module components can access both stores, one using the normal connect
and one using a localConnect
.
The difference between the 2 Provider
and connect
are basically the name of the context
object: store
vs localStore
.
Conclusions
If we agree on that, we might make Provider
and connect
somehow configurable to define the name of the store object, falling back to the default store
.
I’d like to hear your thoughts now, whether this is a correct approach, whether I’m missing some piece of the puzzle or whether there are “better” alternatives.
Thanks! 😃
Issue Analytics
- State:
- Created 8 years ago
- Reactions:8
- Comments:13 (5 by maintainers)
Top GitHub Comments
I finally had the time to publish this as a library, in case someone is interested.
https://github.com/emmenko/react-redux-custom-store
Why is this a problem? A nested
<Provider>
will override the context supplied by the parent<Provider>
. Context can be nested.