The Redux Shortcut; One Action, One Reducer, No Dispatch!
Tags: Firebase, JavaScript, React Native, Web Development, Redux

The Beginning

When I first started with redux this is what it looked like

The near infinite setup; Over 60 types, dispatch after dispatch and an AI’s worth of state cases

And gosh was it a hassle! Switching between a dozen files to create new actions, setup new types and add new cases to the reducer just to manage one new piece of redux state! Then there’s dispatch! And then extra complexities when it comes to something as simple as routing to a new section of your app when you are dealing with an asynchronous function with an action. But if there is an easier way to handle all this, I may have just stumbled across it. So how then do we go from the spider web above, to this below

The finite setup; One action, one reducer, no dispatch

*this is not an actual redux tutorial so i’ll go ahead and assume you already know how to set things up *

Setting up the Loom.

So, the idea of this “loom” setup is to use a single action to accept an object of data and liaise between the components and the reducer. One action, one type. Will look like this;

//types.js

export const SET_APP_STATE = 'set_app_state'; //action.js import {SET_APP_STATE} from './types'; export const setAppState = object => { return { type: SET_APP_STATE, payload: object, }; };

And just like that we are done with our actions and types. Nice and easy.

Now for our beloved reducer.

//Reducer.js import {SET_APP_STATE} from '../actions/types'; const INITIAL_STATE = { * some initial state * }; export default (state = INITIAL_STATE, action) => { switch (action.type) { case SET_APP_STATE: return {...state, ...action.payload}; default: return state; } }; //You can choose to get rid of the switch case since its only one action and one type but i'll maintain it for familiarity's sake.

So, what’s happening with our reducer? Whenever our setAppState action is called, we spread out the current contents of our redux state, and then spread in the new contents of our action.payload — replacing our old state’s content where conflicted and adding any new values appropriately. Same old same old.

Now let’s take a look at how this comes together with an example component.

//Login.js //import action //export with connect //mapStateToProps <TextInput value={this.props.appState.displayName} //access 'displayName' value in redux state placeholder="User name" onChangeText={ userName => { this.props.setAppState({ //action creator to modify redux state displayName: userName //set new 'displayName' value in redux state }); }} />

Look familiar? setState? setAppState? get it? So, for a simple text input like this, we handle its onChangeText event with

this.props.setAppState({stateKey: stateValue})

and read its value from our redux state with

this.props.appState.stateKey

from all components within our application. Looking good right? Almost exactly like you would use the react component level setState() and this.state commands.

Goodbye dispatch!

But that’s not all. Apart from this being a much simpler and relatively more finite kind of setup, my favourite part is that it completely does away with dispatch while handing us finer control over our state. For instance, consider this user authentication function

//Login.js loginUser() { this.props.setAppState({loading: true}); //use action creator to set loading flag to true and start any activity indicators const {email, password} = this.props.appState; //get email and password values from redux state bucket firebase .auth() .signInWithEmailAndPassword(email, password) .then((user)=>{ this.props.setAppState({// disable loader and set currentUser.displayName to redux state loading: false, displayName: user.displayName, }); this.props.navigation.navigate('Splash') } .catch(error => this.props.setAppState({ errorMessage: error.message,// set error messages to redux state loading: false,// stop loading/ disable activityIndicator }), ); }

Typically, a function like this would involve a number of dispatches to send off types and payloads to our reducer in order to modify our state at various levels of the code block. Setting up redux this way however, means functions like these no longer require unique action creators, dispatches and redux cases of their own to manage state but can be written up as ordinary helper methods within our components.

The Catch.

As awesome as all this probably sounds, there is one small catch to it all. Unlike how React’s component state management is able to recursively handle nested objects and arrays, I haven’t quite figured out how to similarly spread out each nested object or array but I guess now it’s a question for the community. At the moment, however, I usually circumvent this issue in one of two ways.

//option 1 this.props.setAppState({ object: { ...this.props.appState.object, modifiedKey: modifiedValue, newKey: newValue } })

One way is to spread in the current contents of the nested element into a new object/array before adding any new or modified values to it. This works fine for a shallow nest but if you have to go deeper, the next option does one better.

//option 2 //new type //new action //new case

This is another reason I decided to maintain the switch case format of the reducer. For deeply nested elements, we can choose to create one more action, type and case to manage their state and is most ideal if we need to perform a lot of operations within them.

I expect it to be possible to set things up much like the react local component level state management which allows nesting without these added complexities but I am probably just too much of a noob to figure it all out on my own.

Personally, I believe I was either going about using redux completely wrong in the beginning or this is something we could all use to ease up some stress when it comes to working with redux state management with React Native. Either way, I look forward to receiving responses from redux experts/veterans about the pros and cons of this kind of setup.

Happy Hacking!

Demo project GitHub

https://github.com/Bimpong/reduxShortcut