Skip to content

Instantly share code, notes, and snippets.

@bodhi
Created February 25, 2016 01:38
Show Gist options
  • Save bodhi/38e2f02be6de71484d73 to your computer and use it in GitHub Desktop.
Save bodhi/38e2f02be6de71484d73 to your computer and use it in GitHub Desktop.
Redux is a central dispatcher

I want to do something based on an action being dispatched, rather than the state being a given value. For example:

  • Reload the timeline immediately after I log in on my Twitter client
  • Retrieve data about a notification immediately on receiving a notification

I could set up a reactive signal or promise or something like that and pump events into it, and dispatch events into Redux. But I already have a place that I'm dispatching actions into: the Redux store.

It reminds me of Yaron Minsky's blog post with some discussion about edge-triggering vs level-triggering.

(waitForState is a bit overengineered for this example, compared to waitForAction)

const listeners = []
// Do it with middleware instead
const eventStream = store => next => action => {
next(action); // Apply the action to the store
listeners.forEach(l => l(action));
}
function addListener(fn) {
listeners.push(fn)
}
import { store } from './store'; // Store has listenerMiddleware
import { refreshTimeline } from './actions/timeline';
import { LOG_IN } from './constants';
addListener(action => {
if (action.type === LOG_IN) {
store.dispatch(refreshTimeline())
}
})
import { store } from './store';
import { refreshTimeline } from './actions/timeline';
// Return a promise that resolves when fn returns a truthy value
const waitForState = store => fn => {
let resolve;
let promise = new Promise(r => {
resolve = r
});
const unsubscribe = store.subscribe(() => {
const s = store.getState();
let result = fn(s);
if (result) {
resolve(result);
unsubscribe();
}
});
return promise;
}
function isAuthenticated(state: State): bool {
// ...
}
// Use like this
waitForState(store)(isAuthenticated)
.then(store.dispatch(refreshTimeline()))
@bodhi
Copy link
Author

bodhi commented Feb 25, 2016

I think the end result is the same, the semantics & expressed intent are different:

  1. With the state/subscription

    when the authentication state toggles from false to true, dispatch refreshTimeline

  2. With the action

    when the action LOG_IN is dispatched, dispatch refreshTimeline.

It's more natural/idiomatic in Redux to use #1, indeed, the library and surrounding documentation focus on Redux as a state container.


What I'm interested in is exploring the action dispatch mechanism as a first-class citizen in an application, rather than being relegated to "the way to (eventually) trigger a state change". To abuse Haskell-ish definitions (also stealing Eff from PureScript), conventionally:

Reducer:: State -> Action -> State
Store :: Reducer -> State -> Action -> Eff State
theStore = Store myReducers initialState

but store is a specialisation of "something that accepts actions and turns them into side-effects"

Dispatcher a :: Action -> Eff a
Store :: Reducer -> State -> Dispatcher State

(I'm a bit of an FP novice, so this might not make much sense?)

To experiment, how about redux-thunk? Thunk is "something that takes a store and will eventually dispatch actions into that store"

Thunk :: Dispatcher a -> Eff a

Er, I'm getting lost!

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