How to run an action from outside a connected component
Similar to #75
You sometimes need to run an action from outside a connected component. I haven't seen a recommended pattern to do this so have come up with a temporary solution
This is basically mapActions but scoped to the current module's actions
export function bindActions(store) {
const innerActions = actions(store);
let mapped = {};
for (let i in innerActions) {
mapped[i] = store.action(innerActions[i]);
}
return mapped;
}
// usage
bindActions(store).setActiveOrder(order);
This also works, but uses a string to reference the action name
export const runAction = (store, actionName, args) => store.action(actions(store)[actionName])(args);
// usage
runAction(store, 'setActiveOrder', order);
Any thoughts on a nice way to do this?
Agreed. I wish mapActions were exposed directly, but I did something like your first, but instead of current module's actions, taking both state and actions as parameters to make it reusable.
Might be worth taking a peek at Stockroom. It extends Unistore with this functionality in order to make actions centralized (since they're running in a single Web Worker).
The key is to replace store.action() with an implementation that pulls actions by name rather than expecting a mapping/mapping function.
It seems like we have a fairly decent surface area for this already. Most notably, it's a bit hard to provide a simple API for bindActions, since it requires a Store reference due to the store not being a singleton.
I tend to use things like your example above, or just throw the .action() call inline:
import { add, subtract } from './actions';
store.setState({ value: 5 })
store.action(add)(5)
store.getState().value // 10
The following is not really a 'pattern' as such, but it's certainly a simple way that's supported well by the API:
const store = createStore({
lols: 5,
});
const actions = (store) => ({
myCoolAction(state) {
return { ...state, lols: state.lols+1 }
},
});
store.setState(actions(store).myCoolAction(store.getState()));
Here's the pattern I settled on:
const store = createStore({
ready: false,
lols: 5,
});
const actions = (store) => ({
increment(state) {
return {...state, lols: state.lols+1 }
},
setReady(state, ready) {
return {...state, ready: ready, }
},
});
function act(store, actionsObj, action, ...args) {
return store.setState(
actionsObj[action](
store.getState(),
...args));
}
const actor = act.bind(null, store, actions(store));
const boundActions = Object.assign({},
...Object.keys(actions(store))
.map(action => ({
[action]: actor.bind(null, action),
})),
);
boundActions.increment();
boundActions.setReady(true);