Skip to content

Instantly share code, notes, and snippets.

@gaearon
Last active January 21, 2025 08:07
Show Gist options
  • Save gaearon/eeee2f619620ab7b55673a4ee2bf8400 to your computer and use it in GitHub Desktop.
Save gaearon/eeee2f619620ab7b55673a4ee2bf8400 to your computer and use it in GitHub Desktop.
Breaking out of Redux paradigm to isolate apps
import React, { Component } from 'react'
import Subapp from './subapp/Root'
class BigApp extends Component {
render() {
return (
<div>
<Subapp />
<Subapp />
<Subapp />
</div>
)
}
}
// These subapps will be completely independent.
//
// They won't share data or actions, and won't see or communicate with each other.
// If you mix this approach with standard Redux approach of composing reducers, it
// will get extremely confusing so it's best if you pick just one: either your app
// is composed of pieces that follow Redux pattern holistically, or your app is so
// large and disjointed that smaller independent "subapps" make more sense.
//
// The first case is probably closer to normal web products, and the second case is
// closer to a "product hub", a "dashboard", or enterprise software where unrelated
// tools are grouped together because they're part of one package.
// This is our subapp's root connected component.
// It can render more components, connected or not, below, just like normally.
// Usually we'd render it in <Provider> and be done with it.
class App extends Component { ... }
export default connect(mapStateToProps)(App)
// However we don't have to call ReactDOM.render(<Provider><App /></Provider>)
// if we're interested in hiding the fact that it's a Redux app.
//
// Maybe we want to be able to run multiple instances of it in the same "bigger" app
// and keep it as a complete black box, with Redux being an implementation detail.
// To hide Redux behind a React API, we can wrap it in a special component that
// initializes the store in the constructor. This way every instance will be independent.
//
// Note that this is *not* recommended for parts of the same app that share data.
// But it can be useful when the bigger app has zero access to smaller apps' internals,
// and we'd like to keep the fact that they are implemented with Redux an implementation detail.
// Each component instance will have its own store, so they won't "know" about each other.
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import reducer from './reducers'
import App from './App'
class Root extends Component {
constructor(props) {
super(props)
this.store = createStore(reducer)
}
render() {
return (
<Provider store={this.store}>
<App />
</Provider>
)
}
}
@radosny
Copy link

radosny commented Mar 26, 2018

@jochakovsky I think in that case I will use createProvider and your own connect for <SubApp/>. <SubApp/> container will be taking selected props and actions from <BigApp/> connect and from your sub-store with a custom one.
For example (pseudocode):

import { Provider, connect } from 'react-redux';

const coreStore = createStore(coreReducers);
const SubProvider = createProvider('sub');
const subStore = createStore(subReducers);

const SubAppContainer = compose(
    withSubStore('sub')(mapSubStoreStateToProps, mapSubStoreDispatchToProps),
    connect(mapStateToProps, mapDispatchToProps),
)(subAppView);

<Provider store={coreStore}>
    <Router> 
        <Route path='/sub'> // I assume that your SubApp will be loaded dynamically on route change
            <SubProvider store={subStore}>
                 <SubAppContainer/>
            </SubProvider>
        </Route>
    </Router>
</Provider>

@shaibam
Copy link

shaibam commented Sep 15, 2019

@jochakovsky I think in that case I will use createProvider and your own connect for <SubApp/>. <SubApp/> container will be taking selected props and actions from <BigApp/> connect and from your sub-store with a custom one.
For example (pseudocode):

import { Provider, connect } from 'react-redux';

const coreStore = createStore(coreReducers);
const SubProvider = createProvider('sub');
const subStore = createStore(subReducers);

const SubAppContainer = compose(
    withSubStore('sub')(mapSubStoreStateToProps, mapSubStoreDispatchToProps),
    connect(mapStateToProps, mapDispatchToProps),
)(subAppView);

<Provider store={coreStore}>
    <Router> 
        <Route path='/sub'> // I assume that your SubApp will be loaded dynamically on route change
            <SubProvider store={subStore}>
                 <SubAppContainer/>
            </SubProvider>
        </Route>
    </Router>
</Provider>

the annoyance of using this method is that in the official redux documentation states - "You probably only need this if you are in the inadvisable position of having multiple stores".

https://react-redux.js.org/5.x/api/provider

@Nathaniel-Fernandes
Copy link

Thanks for this nice Gist! But I wonder how to dispatch actions to the component's own special store? Is there a way to access the store-prop of the <Provider /> for each <SubApp /> (and their child-components)?

Has anyone figured this out yet?

I am using react to build components for a WordPress site. Each 'root' component will need its own state, but how would I use useDispatch and useSelector to get each component's appropriate state?

@pycraft114
Copy link

@Nathaniel-Fernandes @gaearon any advice with dispatching scoped actions?

@amir-gorji
Copy link

How about using a subApp as a child of another subApp like this?

return(
   <SubApp1>
      <SubApp2/>
   </SubApp1>
)

Suppose the SupApp2 is a calendar app or something which should be used inside of SubApp1, but there are no shared components between these 2 subApps.
Is it possible?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment