Redux is awesome! But people often complain about how much boilerplate they have to write when working with it. Part of this problem, is because they feel unproductive defining , and big , but also because they don’t have a clear idea on how to organize their project, or even how to proper handle async requests. On this post, we are going to talk about those concerns and present some approaches to handle them. constants action creators reducers About Redux Arc is a tiny and well tested lib to help you to be productive on your daily work with Redux. It has utilities to abstract the creation of , , and it also has an elegant way to manage . It’s also worth mentioning that it has been used for almost a year in production, without regretting a single day! Arc action types action creators reducers async requests First things first, let’s talk about creating actions! The process about creating actions When we are talking about , usually, the first step we take when we are going to create a new feature is: Create and . Redux action types action creators Let’s say we are creating a Contacts app and the first feature we have to implement is the creation of a contact. The and the action for that would look like this: type creator CREATE = ; create = ({ : CREATE, : { name, email, phone, }, }); export const 'CONTACTS_CREATE' export const ( ) => name, email, phone type payload Then, you import the to use it in your reducer and import the to use in your component. action type action creator The above code is fine, but it’s unlikely that you would have only a feature. In a real world, you would have at least a . So, let’s see the code for that: creation CRUD CREATE = ; READ = ; UPDATE = ; REMOVE = ; create = ({ : CREATE, : { name, email, phone }, }); read = ({ : READ, : { id }, }); update = ({ : UPDATE, : { name, email, phone }, : { id }, }); remove = ({ : REMOVE, : { id }, }); export const 'CONTACTS_CREATE' export const 'CONTACTS_READ' export const 'CONTACTS_UPDATE' export const 'CONTACTS_REMOVE' export const ( ) => name, email, phone type payload export const ( ) => id type meta export const ( ) => id, name, email, phone type payload meta // delete is a reserved word export const ( ) => id type meta The above code is very simple, but as you can see, it feels like we are repeating code. If it feels like that in a Contacts CRUD, imagine in an application with dozen of modules. If you are familiar with , you know we should avoid code repetition. You may say we are not repeating code, because each creator has it’s own “business logic” and is creating a different kind of action, but I beg to disagree. If you really pay attention, you can see the patterns: DRY As we are following spec, every time we want to send a content with our action, we should keep it in the . Any additional meta info, should go under . If our action indicates an error, should be true and should contain the actual error. Flux Standard Action action.payload action.meta action.error action.payload we are always defining actions and with the same name, the only difference is that one is uppercased and the other is camel cased. types creators Knowing the first pattern, we could try to normalize our creators, changing them slightly to work with the fixed arguments: and . payload meta Let’s take the action as an example, instead of having , and as arguments, we could have just the : create name email phone payload CREATE = ; create = ({ : CREATE, payload, }); export const 'CONTACTS_CREATE' export const ( ) => payload type About the second pattern, the action type could be generated based on the creators name. Now that we can normalize our and we know that the could be generated based on ’s name, it’s completely possible to create a factory that given a config it generates the and the for us. creators types creator creators types A factory with a simple api generating creators and types would be very handy, right? That is exactly what you have on Arc’s : createActions ( ) { createActions } = ( ); { creators, types } = createActions( , { : , : , : , : , }); require "redux" const require 'redux-arc' const 'contacts' create null read null update null remove null The function expects a namespace as its first argument and an action definition object as the second. createActions For the action definition object, each should be the creator name and each could be either, an object with the defaults for , and or if you don’t want any defaults. key value payload meta error null The result will be an object, containing the and the for each action. creators types Types The types from the above config, would look just like the following: { : , : , : , : , } CREATE 'CONTACTS_CREATE' READ 'CONTACTS_READ' UPDATE 'CONTACTS_UPDATE' REMOVE 'CONTACTS_REMOVE' : the namespace we provided in the config, was used to prefix the action types value, in order to avoid having two different actions with the same name in the application Notice . Based on the above object, wherever you would like to use the type , you could just import the object and use it such as below: CREATE types { types } ; { (action.type === types.CREATE) { } state; } types.CREATE import from './actions.js' ( ) function myReducer state, action if // do something return // CONTACTS_CREATE Creators Based on our CRUD config, the creators object would be similar to this: creators = { : {...}, : {...}, : {...}, : {...}, }; const create ( ) function payload, meta, error read ( ) function payload, meta, error update ( ) function payload, meta, error remove ( ) function payload, meta, error Creators creates the actions using the payload, meta and error arguments. They are all optionals, so, you can omit them as you like. Take a look at how we would use our creator: create { creators } payload = { : , : , : }; creators.create(payload); import from './actions.js' const name 'Luke' email 'luke@jedimasters.co' phone '421 421 421' /* { type: 'CONTACTS_CREATE', payload: { name: 'Luke', email: email: 'luke@jedimasters.co', phone: '421 421 421', } } */ The creators only generates the actions, so, whenever you want to dispatch an action, you have to use it combined with the method from Redux store, as we are going to see below. dispatch Using Action Creators in the components The most common place we use creators is inside a component connected to the store. There’s no secret when you have to use a creators generated by arc inside a component. Take a look at the example: React, { Component } ; { connect } ; { creators } ; { } mapStateToProps = ({ }); mapDispatchToProps = ({ : { dispatch(creators.create(formValues)); }, }); connect( mapStateToProps, mapDispatchToProps, )(ContactsForm) import from 'react' import from 'react-redux' import from './actions' class ContactsForm extends Component // {...} const ( ) => state /* map your state*/ const ( ) => dispatch create ( ) => formValues export default As you can see in the above example, we just defined a method inside , to have access to method. Then, inside our component we would have access to through the props. create mapDispatchToProps dispatch create .props.create(formValues); this Creating Reducers As you already know, we are going to reuse the generated inside our . But, that are also other bits we have to discuss about reducers. types reducers Redux calls all the application reducers when we dispatch an action, then, inside the reducers we have to check if the dispatched action means something to the given reducer or not. I’ve seen many people using switch cases to handle that situation, where you match the action with your and you can use default case to return the previous state. The problem is that switch cases doesn’t scale very well, and you can end up with painful code to maintain. type case I have seen also many people using multiple IFs, including me, but this approach has similar issues with the switch case’s approach. With both approaches, you end up dealing with matching logic and state changes in the same place. That way is hard to focus in a small piece of code per time. Thinking about that, we created a createReducers function in , which accepts an and a object which the keys are the action types and the values are handlers for each action. Take a look at the example below: Arc initial state handlers { createReducers } ; { types } ; INITIAL_STATE = []; onCreate = [ ...state, action.payload, ]; onRead = state.find( contact.id === action.meta.id); onUpdate = state.map( contact.id !== action.meta.id ? contact : action.payload ); onRemove = state.filter( contact.id !== action.meta.id); HANDLERS = { [types.CREATE]: onCreate, [types.READ]: onRead, [types.UPDATE]: onUpdate, [types.REMOVE]: onRemove, }; createReducers(INITIAL_STATE, HANDLERS); import from 'redux-arc' import from './actions' const const ( ) => state, action const ( ) => state, action => contact const ( ) => state, action => contact const ( ) => state, action => contact const export default The main idea about this approach, is having a handler for each kind of action. That way you can focus in a small chunk of code per time, instead of a function with 100 lines. It also has a better performance in comparison to IFs and switch cases, as it uses short circuit verification. Under the hood its code is similar to the following: { { handler = handlers[action.type]; !handler ? state : handler(state, action); } } ( ) function createReducers initialState, handlers return ( ) => state = initialState, action const return As you can see, there’s no magic in the code, all we are doing is verifying if there’s a handler to the given action. If we do, we just call it providing the state and the action, if we don’t, we just return the previous state. The difference is that you have this abstracted and tested for you, with validation as a bonus, to reduce your debugging time in case you provide an invalid action type or an invalid handler: Creating Async Requests has also a good support to help you handle async requests as it was originally designed to do just that. I plan to explore this topic in another post, but if you are interest on this, take a look at the docs to see how you can use it: . Arc redux-arc.js.org/#async-actions Recap The function accepts a config and returns and createAction types creators. You can import and use them with . types createReducers allows you to turn your reducer into small handlers. createReducers You can import and use the inside your components or wherever you need to dispatch an action. creators Useful links . Contacts CRUD in Redux Arc Docs : . redux-arc.js.org . Gitub repository Conclusion Besides reducing boilerplate and allowing you to split your code elegantly, as well as is all about configuration over programmatically implementation. That allows us to run validations to help you with debugging and also allows you to focus on what really meters. createReducers createActions You don’t want to spend time writing boilerplate or debugging your whole application just because you commit a typo. You want to implement features and fix bugs as better and faster as it’s possible, without loose flexibility and we want to help you do that! Any feedbacks and contributions are very welcome. Feel free to to ask me more about it. open an issue on github Did you enjoy the read? ️❤️ Help us spread the word by giving a like and sharing️️️️ ❤️ 🖖 Don’t forget to follow me, to be notified about future posts! 🖖