redux-zero / redux-zero

A lightweight state container based on Redux

Home Page:https://matheusml1.gitbooks.io/redux-zero-docs/content/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Is there a way to dispatch actions from within an action ?

esperancaJS opened this issue · comments

In more complex Actions it would make the code simpler to separate it into other actions.

Wondering if there is a simple way to do this already.

Thanks!

Currently I've found this solution:

const mapActions = ({setState}) => ({
  startComplexFlow(state) {
    setState({ startComplexFlowLoading: true });

    // dispatching an action within an action
    mapActions({setState}).continueComplexFlow(state);

still curious to see how it'll work with Kiho/redux-zero@d63baca

@matheusml

Does the following looks like a good idea ? Or can you foresee some obvious pitfalls ?

Plot twist : there is no need to change anything in redux-zero to make it work.

It's nothing more than a wrapper around actions - just like bindActions.

./ConnectedComponent.js

import {connect} from 'redux-zero/preact'
import chainedActions from './chainedActions'
import actions from './actions'
....
export default connect(mapState, chainActions(actions))(Component)

Basically, an object containing all actions is passed down as a third argument to each individual action.

So we can now 'chain' our actions like below :

./actions.js

export default (store, ownProps) => ({

	test0: (state, args, actions) => {

		actions.test1(0)

		return {test: 0} // should return before test1
	},

	test1: async (state, args, actions) => {

		console.log(args) // should log 0

		await actions.test2()

		return {test: 1} // should return after test2
	},

	test2: (state, args, actions) => {

		actions.test3(2, 1000) // should 1s after test2

		return {test: 2}
	},

	test3: async (state, args, actions) => {

		console.log(args) // should log 2

		await actions.test4(null, 1000) // should wait for 1s

		return {test: 3} // should return immediately after test4
	},

	test4: (state, args, actions) => ({test: 4})
})

./chainActions.js
EDIT: mapState & mapAction help me to 'namespace' my store - but that's beyong the scope of this issue

export default userActions => 

	(store, ownProps, mapState = state => state, mapAction = res => res) => {

		const actions = {}

		userActions = userActions(store, ownProps)

		const chainActions = () => {

			const actions = {}

			for(let k in userActions) {
				actions[k] = (arg, ms = 0) => 
					new Promise(res => 
						setTimeout(async () => {
							store.setState(
								mapAction(
									await userActions[k](
										mapState(store.getState()),
										arg,
										chainActions()
									) || {}
								) 
							)	
							res()
						}, ms))
			}

			return actions

		}

		for(let k in chainActions()) {
			actions[k] = async (state, arg) => mapAction(
				await userActions[k](mapState(state), arg, chainActions()) || {}
			)
		}

		return actions

	}

Sorry for the long post & the funny indentation.

I think they're both good approaches to the problem, I just don't see why you should do this in the first place.
If your actions is too big, it should probably be splitted anyway.