Evheniy Bystrov

@evheniybystrov

React/Redux development on steroids

In this article I’ll show how fast to develop React / Redux app using Redux Lazy.

Redux Lazy is a wrapper for redux stuff (action types, action creators, reducers, containers…). It’s like recompose for redux.

If you are working with recompose you know that it’s a package of useful high order components (HOC). But before using created HOCs by other developers you should know how to create a simple React component, how to compose it with other components and how to create own HOC.

The same with Redux Lazy. It’s not a new store library. It’s just a wrapper, with a little bit magic (yes, I know that magic in code is not a good practice) you can save up to 50% of time on creating React / Redux app.

Before start describing Redux Lazy I want to show the main parts of working with Redux.

Redux is a store. You can read a lot of manuals and articles with documentation or good practices. But store is always a store. It’s a place where you can save your data.

For redux to get data you should create selectors (like SQL SELECT) for react-redux connect HOC and action types, action creators and reducers to set data using store dispatch.

So I’ll show each point of working with redux here:

Action types:

export const NAME_SPACE_TEXT = '@@nameSpace/TEXT';

Here I just created one action type. It should help with storing text field to store. Name space I use to split a big app into small modules with own components, store (using combineReducers) and logic (I use redux-observable).

Action creator:

import { NAME_SPACE_TEXT } from './types';
export const textAction = text => ({
type: NAME_SPACE_TEXT,
text,
});

Reducer:

import { NAME_SPACE_TEXT } from './types';

const defaultState = {
text: '',
};

export default (state = defaultState, action) => {
switch (action.type) {
case NAME_SPACE_TEXT:
return { ...state, ...action };
default:
return state;
}
};

This is a simple reducer. The main idea is just check action type and if it’s our text action, create a new state using previous state with action.

Container:

import { connect } from 'react-redux';
import * as actions from './actions';

const mapStateToProps = state => state[NAME_SPACE];
const mapDispatchToProps = { ...actions };

export default connect(mapStateToProps, mapDispatchToProps);

Here I use the same name space to get part of store in my module. mapDispatchToProps helps me to wrap my action creators with store dispatch function.

I need to add reducer to store:

import { combineReducers } from 'redux';
import nameSpace from './reducer';
const rootReducer = combineReducers({
...
nameSpace,
});

And wrap component by the container:

import Container from './container';
import Component from './component';
export default Container(Component);

That’s all. Each time to make a new feature you should create types, creators, reducers, containers… instead of thinking about your app logic.

To save my time I created Redux Lazy. It can make all this stuff for your. You need to describe which actions do you need and get it.

So let me introduce Redux Lazy. To use it you should make some small steps.

First step is installing redux-lazy:

npm i -S redux-lazy

Or with yarn:

yarn add redux-lazy

The second step is creating creating Redux Lazy model:

import RL from 'redux-lazy';

const rl = new RL('modelName');
const {
nameSpace,
types,
actions,
defaultState,
reducer,
mapStateToProps,
mapDispatchToProps,
Container,
} = rl.flush();

Here we should set a name space of our model. For example I use modelName name space.

And it creates nameSpace, types, actions, reducer, container… for you.

Next you should think which fields in store do you need. Imagine that you are working with SQL table. You need to have name for it (than’s why I use modelName name space) and fields.

For example, from the previous example, we need only one field — text. So your job is making getter and setter for this field.

addEventAction(name)

If you need to send event to redux-observable you need to create action type with only one field: type. And epics can run some jobs for you.

import RL from 'redux-lazy';

const rl = new RL('modelName');
rl.addEventAction('event');
const { types, actions } = rl.flush();
const { MODEL_NAME_EVENT } = types;
const { eventAction } = actions;
export { MODEL_NAME_EVENT, eventAction };
export default rl;
eventAction() // => { type: MODEL_NAME_EVENT }
eventAction(anyData) // => { type: MODEL_NAME_EVENT }

addFormAction(name);

If you need to submit form with redux actions you need to work with events and put function into form component. There is a good practice to use links instead of objects or functions inside a components. Because it always creates a new instances and React should render again component even if data is the same.

<form onSubmit={(event) => {
event.preventDefault();
props.submitAction();
}}>

With Redux Lazy you can avoid useless rendering:

rl.addFormAction('submit');
<form onSubmit={props.submitAction}>

addFormElementAction(name, defaultValue)

The same for form elements. Each time you should get value from event (event.target.value):

<input
type="text"
onChange={event => props.titleAction(event.target.value)}
value={props.title}
/>

With Redux Lazy you can make it much easy:

rl.addFormElementAction('title', '');
<input
type="text"
onChange={props.titleAction}
value={props.title}
/>

addParamAction(name, defaultValue)

It’s the same like addFormElementAction but with simple data instead of event object.

It’s totally the same like any redux action creator:

rl.addParamAction('title', 'defaultValue');
const { actions } = rl.flush();
const { titleAction } = actions;
titleAction('data')// => {type: '@@modelName/TITLE', title: 'data'}
titleAction()// => {type: '@@modelName/TITLE', title:'defaultValue'}

addParamsAction(name, payload)

If you need to set more than one field you can use addParamsAction:

rl.addParamsAction('clear', { title: '', body: '' });
const { actions } = rl.flush();
const { clearAction } = actions;
clearAction('1', '2') // => { title: '1', body: '2' }
clearAction('1') // => { title: '1', body: ''}
clearAction() // => { title: '', body: '' }

Redux Lazy really helps me to speed up my React / Redux development. But some times it’s hard to get all actions, types…

I know how it works and what I can get from rl.flush(). But first time maybe you can run console.log(rl) and see what types, actions, nameSpace… are inside.

I think it’s not a great payment for development speed.

You can see more examples.

Please ask your questions in comments and don’t forget to star it on github.

More by Evheniy Bystrov

Topics of interest

More Related Stories