Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vasanthk/26bf91b384dd645d5f94ed2e688ee6c9 to your computer and use it in GitHub Desktop.
Save vasanthk/26bf91b384dd645d5f94ed2e688ee6c9 to your computer and use it in GitHub Desktop.
Use Redux ecosystem without Redux
////////////////////////////////////////////////////////////////////////
// Intro
///////////////////////
// Tools like Redux-saga, React-redux and Reselect can easily be used without Redux
// For Reselet there's nothing to do, it's just not coupled to Redux
// For the others, you just need to provide an adapter
// At Stample.co we use a legacy framework that is quite close to Redux but with a bad API
// We want to progressively migrate to Redux, so starting now to use Redux tools on new features will make our migration faster
// Our legacy framework: https://github.com/stample/atom-react
// This framework, like Redux, has an unique immutable state object, and you can dispatch events and register event listeners
// It is quite similar to many Flux implementations so I believe you can adopt the same strategy if you plan to migrate to Redux
////////////////////////////////////////////////////////////////////////
// Redux-saga
///////////////////////
// To be able to use redux-saga, you just need a system that listen to and publish events
// Hopefully Yassine has worked on my project and Redux-saga to make it flexible enough and being able to work in other contexts
// You can provide your own "reduxSagaIO" if the default one (targeting Redux) does not satisfy you
// See also https://github.com/yelouafi/redux-saga/issues/16
import { runSaga } from 'redux-saga'
const reduxSagaIO = {
// publishEvents() is the equivalent of dispatch() for our legacy framework
dispatch: events => context.publishEvents(events),
subscribe: listener => {
const callback = event => {
// Note that if you'd like simple action matching like take("ACTION_NAME"), you should be sure that your actions has a type property.
// It was not the same in our framework where we had action.name so we add a type automatically here
listener({...event, type: event.name})
}
// addEventListener() is the equivalent of subscribe for our legacy framework
context.addEventListener(callback)
// removeEventListener() is how we unsubscribe in our legacy framework
return () => context.removeEventListener(callback)
}
}
runSaga(saga,reduxSagaIO)
////////////////////////////////////////////////////////////////////////
// React-redux
///////////////////////
// To be able to use React-redux, you need the same publish/subscribe as redux-saga requires
// but you also need access to the global state object
// The connect() method does not require a Redux store to be inside React-context, it only requires a store in context that actually has the Redux-store API
// If you build an adapter to your framework with the same API, it will also work
// Here's how I transform an atomReactContext to a reduxStoreLike
function toReduxStoreAPI(atomReactContext) {
// Listeners that will be called on every state change, like Redux does
const onStateChangeListeners = []
// This in our legacy is run just after state changes, but just before we try to render it from the very top
// As we want to move to Redux, we don't want to render from the top anymore but instead render nested components directly with connect()
atomReactContext.beforeRender(() => {
onStateChangeListeners.forEach(listener => listener())
})
// We return an object that has the same API as a Redux-store, so it is compatible with connect()
return {
subscribe: listener => {
onStateChangeListeners.push(listener)
var isSubscribed = true
return function unsubscribe() {
if (!isSubscribed) {
return
}
isSubscribed = false
var index = onStateChangeListeners.indexOf(listener)
onStateChangeListeners.splice(index, 1)
}
},
dispatch: event => {
return atomReactContext.publishEvent(event)
},
// in our case, we have a single state object so it's very similar to Redux
// if you want to do the same but have many singleton Flux stores, you can easily aggregate them in a single object here
getState: () => {
return atomReactContext.getState()
}
}
}
// Usage is simple like if you were using a normal Redux-store. You have to use the provider, and that's all.
import { Provider } from 'react-redux'
const Main = React.createClass({
render: function() {
return (
<Provider store={toReduxStoreAPI(atomReactContext)}>
<AppMainLayout/>
</Provider>
)
}
})
// Then you can use connect to get your data injected anywhere
// You can also use Reselect without anything else to do
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment