Create a gist now

Instantly share code, notes, and snippets.

@gaearon /index.js
Last active Jul 10, 2018

Embed
What would you like to do?
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>
)
}
}
@JonathanGTH

This comment has been minimized.

Show comment
Hide comment
@JonathanGTH

JonathanGTH Apr 2, 2016

Nice Work Dan

Nice Work Dan

@slorber

This comment has been minimized.

Show comment
Hide comment
@slorber

slorber Apr 2, 2016

nice, but how will DevTools work if there are 3 instances of the same app which may all use the same devtool?

slorber commented Apr 2, 2016

nice, but how will DevTools work if there are 3 instances of the same app which may all use the same devtool?

@StevenLangbroek

This comment has been minimized.

Show comment
Hide comment
@StevenLangbroek

StevenLangbroek Apr 4, 2016

@slorber if you use the Chrome extension you'd have to instrument it in each store, but theoretically that should work. It's already capable of keeping track of stores in different tabs simultaneously.

@slorber if you use the Chrome extension you'd have to instrument it in each store, but theoretically that should work. It's already capable of keeping track of stores in different tabs simultaneously.

@slorber

This comment has been minimized.

Show comment
Hide comment
@slorber

slorber Apr 4, 2016

thanks :) didn't know that as I don't use redux devtools yet unfurtunatly

slorber commented Apr 4, 2016

thanks :) didn't know that as I don't use redux devtools yet unfurtunatly

@zalmoxisus

This comment has been minimized.

Show comment
Hide comment
@zalmoxisus

zalmoxisus Apr 4, 2016

@StevenLangbroek, yes, that would work, also you could name each subapp instance and easily switch between them.

@StevenLangbroek, yes, that would work, also you could name each subapp instance and easily switch between them.

@emmenko

This comment has been minimized.

Show comment
Hide comment
@emmenko

emmenko Apr 4, 2016

FYI: following the example, if you also need to wrap the root application component (e.g. BigApp) into a Provider, you have then a problem the SubApps also have Providers.
As you may guess, the problem is that the stores in the context will conflict as they have the same name store.

To solve that, based on Dan's feedback, I've put together a small library that allows you to define a custom store name, effectively allowing to have nested providers.

https://github.com/emmenko/react-redux-custom-store

emmenko commented Apr 4, 2016

FYI: following the example, if you also need to wrap the root application component (e.g. BigApp) into a Provider, you have then a problem the SubApps also have Providers.
As you may guess, the problem is that the stores in the context will conflict as they have the same name store.

To solve that, based on Dan's feedback, I've put together a small library that allows you to define a custom store name, effectively allowing to have nested providers.

https://github.com/emmenko/react-redux-custom-store

@gopeter

This comment has been minimized.

Show comment
Hide comment
@gopeter

gopeter Aug 5, 2016

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)?

gopeter commented Aug 5, 2016

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)?

@ahmadabdul3

This comment has been minimized.

Show comment
Hide comment
@ahmadabdul3

ahmadabdul3 Sep 1, 2016

Which approach would someone take if each 'subapp' is rendered in a separate page instead of nested in a parent div?

ahmadabdul3 commented Sep 1, 2016

Which approach would someone take if each 'subapp' is rendered in a separate page instead of nested in a parent div?

@ghernandez345

This comment has been minimized.

Show comment
Hide comment
@ghernandez345

ghernandez345 Jun 29, 2017

Nice, but let's say I have a use case where smaller subapps makes sense, but I also need them to communicate with each other? Has anyone tried that? My specific use case is updating a monolith rails app by replacing chunks of the frontend piecemeal into isolated sub apps (eg. navigationApp, chatApp, contentApp).

Nice, but let's say I have a use case where smaller subapps makes sense, but I also need them to communicate with each other? Has anyone tried that? My specific use case is updating a monolith rails app by replacing chunks of the frontend piecemeal into isolated sub apps (eg. navigationApp, chatApp, contentApp).

@tylerwgoza

This comment has been minimized.

Show comment
Hide comment
@tylerwgoza

tylerwgoza Jul 7, 2017

@ghernandez345 has a point. Has anyone ever tried this? In my case I have some small amount of shared state between what really are independent apps. Basic user info needs to be shared to know login status, user name, and account lists, but we have a couple of apps that should remain independent of the global state since nothing else will use that data once the users are done.

tylerwgoza commented Jul 7, 2017

@ghernandez345 has a point. Has anyone ever tried this? In my case I have some small amount of shared state between what really are independent apps. Basic user info needs to be shared to know login status, user name, and account lists, but we have a couple of apps that should remain independent of the global state since nothing else will use that data once the users are done.

@jochakovsky

This comment has been minimized.

Show comment
Hide comment
@jochakovsky

jochakovsky Mar 4, 2018

Having a similar problem to @ghernandez345 and @tylerwgoza, my <Subapp />s need to accept props from <BigApp />, any thoughts on how to handle that elegantly? I described my problem a bit more here: https://stackoverflow.com/questions/49082353/whats-the-best-way-to-call-callbacks-passed-into-a-redux-subapp

jochakovsky commented Mar 4, 2018

Having a similar problem to @ghernandez345 and @tylerwgoza, my <Subapp />s need to accept props from <BigApp />, any thoughts on how to handle that elegantly? I described my problem a bit more here: https://stackoverflow.com/questions/49082353/whats-the-best-way-to-call-callbacks-passed-into-a-redux-subapp

@radosny

This comment has been minimized.

Show comment
Hide comment
@radosny

radosny 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>

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>

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