In this article I’m continue to discuss creating react redux app using redux-lazy. In my experience I found that I spend a lot of time to copy/paste code for working with storage. Each time I should create action types, action creators and reducers. Even the containers are in most cases the same. That’s why I created a new library — redux-lazy. I like to write react app in functional style — I create functional components as a pure functions: **props => JSX**. It helps to test all code, each function has own single responsibility, no side effects, easy to write and support code. As you know ~80% of time we support existing code. So making code more declarative we save our time and money. But first I want to share useful links to my previous articles: [**React — redux for lazy developers** _Each time working with redux in react app we spend a lot of time to make action types, action creators, reducers… Most…_medium.com](https://medium.com/@evheniybystrov/react-redux-for-lazy-developers-b551f16a456f "https://medium.com/@evheniybystrov/react-redux-for-lazy-developers-b551f16a456f")[](https://medium.com/@evheniybystrov/react-redux-for-lazy-developers-b551f16a456f) And [**React: functional way** _As you may know working with react you can use functions or classes — work with stateless and stateful components. In…_blog.cloudboost.io](https://blog.cloudboost.io/react-functional-way-c533fceda2ce "https://blog.cloudboost.io/react-functional-way-c533fceda2ce")[](https://blog.cloudboost.io/react-functional-way-c533fceda2ce) As I use [**redux-observable**](https://redux-observable.js.org/) for side effects I put all logic to [**epics**](https://redux-observable.js.org/docs/basics/Epics.html). React is just a view layer. All redux stuff I use only as getters/setters for store data and events for epics. If you look at the relations: **Component (view)<->Store (DB)<->epics (logic)** You can see that view layer is just a template for showing data from our store (redux). Each change in store we should see in components. And store works not only with view. Each store action we catch in epics and process it to make different stuff like ajax request, change store data (get response and clear form…). To make code and tests I spend each time ~50% to logic, ~20% to components and ~30% to store. Each time to get/set data to store we need to create action types, action creators, reducers and containers up to 30% of our time. After that I tried to save this time and created [**redux-lazy**](https://www.npmjs.com/package/redux-lazy). So next I’ll show how to create classic react redux app, add testing to cover all code and switch redux stuff (types, actions, reducers…)to redux-lazy. But first — I want to repeat: * React is just a view layer, * Pure functions: **props => jsx**, * No other stuff… Redux is our store. The same: * Pure functions (action creators, reducers), * No side effects, * No logic, * Just get/set data to store (CRUD), * All logic in epics. We need to install [**node.js**](https://nodejs.org/uk/), [**npm**](https://www.npmjs.com/). After that install [**yarn**](https://yarnpkg.com/lang/en/) and [**create-react-app**](https://github.com/facebook/create-react-app) like in [**previous article**](https://medium.com/@evheniybystrov/react-redux-for-lazy-developers-b551f16a456f). And create project: mkdir redux-app cd redux-app create-react-app .   And run it: yarn start  Result:  Let’s install [**redux**](https://redux.js.org/) and [**react-redux**](https://github.com/reduxjs/react-redux): yarn add redux react-redux  I like to create single responsibility modules and keep there all code like react components, redux and epics stuff. But first about our app… We will create a form to submit post using [**JSONPlaceholder**](https://jsonplaceholder.typicode.com/) service. And next we will create another module to work with comments. **First step**: we should create directories and empty files. mkdir -p src/modules/post mkdir src/modules/post/components mkdir src/modules/post/containers mkdir src/modules/post/types mkdir src/modules/post/actions mkdir src/modules/post/reducer mkdir src/modules/post/epics touch src/modules/post/index.js touch src/modules/post/components/index.jsx touch src/modules/post/containers/index.js touch src/modules/post/types/index.js touch src/modules/post/actions/index.js touch src/modules/post/reducer/index.js touch src/modules/post/epics/index.js  All code you can find on [**github**](https://github.com/evheniy/redux-app). And our tree:  Don’t forget to install [**redux-logger**](https://github.com/evgenyrodionov/redux-logger). It useful if you need to check current state: yarn add redux-logger And [**prop-types**](https://www.npmjs.com/package/prop-types) — it helps to validate react component properties: yarn add prop-types **Next step**: we need to create store and reducers. Reducers: touch src/reducers.js With code: **import** { combineReducers } **from** 'redux'; **import** post **from** './modules/post/reducer'; **export default** combineReducers({ post }); And store: touch src/store.js With code: **import** { createStore, applyMiddleware } **from** 'redux'; **import** logger **from** 'redux-logger'; **import** reducers **from** './reducers'; **const** store = createStore( reducers, applyMiddleware(logger) ); **export default** store; And update **src/index.js**: **import** React **from** 'react'; **import** ReactDOM **from** 'react-dom'; **import { Provider } from 'react-redux';** **import** './index.css'; **import** App **from** './App'; **import store from './store';** **import** registerServiceWorker **from** './registerServiceWorker'; ReactDOM.render( **<Provider store={store}> <App /> </Provider>,** document.getElementById('root') ); registerServiceWorker(); #### Post module code: To send a post we need to have possibility to change title, body and submit the form. For each point we need to have action type and action creator: #### Action types (src/modules/post/types/index.js): **export const** POST\_TITLE = '@@post/TITLE'; **export const** POST\_BODY = '@@post/BODY'; **export const** POST\_SUBMIT = '@@post/SUBMIT'; To avoid collision I like to add prefix to types like **@@post**/TITLE. #### Action creators (src/modules/post/actions/index.js): **import** { POST\_TITLE, POST\_BODY, POST\_SUBMIT, } **from** '../types'; **export const** titleAction = title => ({ type: POST\_TITLE, title, }); **export const** bodyAction = body => ({ type: POST\_BODY, body, }); **export const** submitAction = () => ({ type: POST\_SUBMIT, }); #### Reducer (src/modules/post/reducer/index.js): **import** { POST\_TITLE, POST\_BODY } **from** '../types'; **const** defaultState = { title: '', body: '', }; **export default** (state = defaultState, action) => { **switch** (action.type) { **case** POST\_TITLE: **case** POST\_BODY: **return** { ...state, ...action }; **default**: **return** state; } }; Here I change state for title and body. To submit form I don’t need to make any actions (changes) with store. #### Container (src/modules/post/containers/index.js): **import** { connect } **from** 'react-redux'; **import** \* **as** actions **from** '../actions'; **const** mapStateToProps = state => state.post; **const** mapDispatchToProps = { ...actions }; **export default** connect(mapStateToProps, mapDispatchToProps); We have a simple container. As we work only with post data, it just takes part of store and all action creators for post. In next article I’ll show how to combine data from different parts of store and memoize it with [**reselect**](https://github.com/reduxjs/reselect). #### Component (src/modules/post/components/index.jsx): **import** React **from** 'react'; **import** PropTypes **from** 'prop-types'; **const** PostComponent = props => ( <form onSubmit={(event) => { event.preventDefault(); props.submitAction(); }}> <h1>Our form example</h1> <div> <input type="text" onChange={event => props.titleAction(event.target.value)} value={props.title} /> </div> <div> <textarea onChange={event => props.bodyAction(event.target.value)} value={props.body} /> </div> <div> <input type="submit" value="Submit" /> </div> </form> ); PostComponent.propTypes = { title: PropTypes.string.isRequired, body: PropTypes.string.isRequired, titleAction: PropTypes.func.isRequired, bodyAction: PropTypes.func.isRequired, submitAction: PropTypes.func.isRequired, }; **export default** PostComponent; Our component is a pure functionm it gets properties and returns JSX code. Here you can see prop types validation. All properties are required because we have post data in store each time from reducer. #### And our post module entry point (src/modules/post/index.js): **import** PostComponent **from** './components'; **import** PostContainer **from** './containers'; **export default** PostContainer(PostComponent); Last thing — we need to update **src/App.jsx** to render our post module: **import** React, { Component } **from** 'react'; **import** logo **from** './logo.svg'; **import** './App.css'; **import Post from './modules/post';** **class** App **extends** Component { render() { **return** ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> **<Post />** </div> ); } } **export default** App; And result:  Not perfect, but this article is not about UI/UX. Let’s check our actions… #### Title changing:  #### Body changing:  #### And submit:  That’s all for now. We just created a simple react-redux app. Next step is testing. I like to use single responsibility packages and create own frameworks for development (yeps, wpb, redux-lazy) and testing. I’ll use next packages: * [**mocha**](https://mochajs.org/) — a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun. * [**chai**](http://www.chaijs.com/) — a BDD / TDD assertion library for [**node**](http://nodejs.org) and the browser that can be delightfully paired with any javascript testing framework. * [**sinon**](http://sinonjs.org/) — standalone test spies, stubs and mocks for JavaScript. Works with any unit testing framework. * [**enzyme**](http://airbnb.io/enzyme/) — a JavaScript Testing utility for React that makes it easier to assert, manipulate, and traverse your React Components’ output. * [**nyc**](https://istanbul.js.org/) — JavaScript test coverage tool. yarn add mocha chai sinon enzyme nyc enzyme-adapter-react-16 -D One small thing. To make production ready app you should not forget about code quality and security. For this stuff I use [**eslint**](https://eslint.org/) with [**airbnb config**](https://www.npmjs.com/package/eslint-config-airbnb) and [**nsp**](https://github.com/nodesecurity/nsp) — node security platform command-line tool: yarn add eslint eslint-config-airbnb eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react nsp jsdom redux-mock-store babel-register -D [**Jsdom**](https://github.com/jsdom/jsdom) is a pure-JavaScript implementation of many web standards, notably the WHATWG DOM and HTML Standards, for use with Node.js. [**Redux-mock-store**](https://github.com/arnaudbenard/redux-mock-store) — a mock store for your testing your redux async action creators and middleware. To run all commands I use [**npm-run-all**](https://www.npmjs.com/package/npm-run-all) — a CLI tool to run multiple npm-scripts in parallel or sequential: yarn add npm-run-all And update package.json scripts section: "scripts": { "start": "react-scripts start", "build": "react-scripts build", "eject": "react-scripts eject", **"test": "npm-run-all test:\*", "test:security": "nsp check", "test:lint": "eslint . --ext .js,.jsx"** }, And some configs (just create those files in project root directory): #### .eslintignore build coveragesrc/registerServiceWorker.js #### .eslintrc { "parser": "babel-eslint", "extends": "airbnb", "env": { "browser": **true**, "node": **true**, "mocha": **true** } } Let’s run it: yarn test  And we see a lot of errors and warnings from eslint. It helps to keep the same code style for all team. After fix:  All changes you can find in [**github repository**](https://github.com/evheniy/redux-app). Don’t forget about [**axios**](https://github.com/axios/axios): yarn add axios I like this client because I can use it on client and backend sides. It can cancel request by timeout. You can even use it as [**graphQL**](https://graphql.org/learn/) client. #### Next step is testing… First, we need to create helper for using jsdom: **tests/helper.js**: require('babel-register')({ presets: \[ 'react', 'env', \], plugins: \[ 'transform-object-rest-spread', \], }); **const** { configure } = require('enzyme'); **const** Adapter = require('enzyme-adapter-react-16'); **const** axios = require('axios'); **const** httpAdapter = require('axios/lib/adapters/http'); axios.defaults.host = 'http://localhost:3000'; axios.defaults.adapter = httpAdapter; configure({ adapter: **new** Adapter() }); **const** { JSDOM } = require('jsdom'); **const** exposedProperties = \['window', 'navigator', 'document'\]; **const** { window } = **new** JSDOM('', { url: 'http://localhost' }); global.document = window.document; global.window = window; Object.keys(document.defaultView).forEach((property) => { **if** (**typeof** global\[property\] === 'undefined') { exposedProperties.push(property); global\[property\] = document.defaultView\[property\]; } }); global.navigator = { userAgent: 'node.js', }; global.localStorage = { setItem() {}, }; global.documentRef = document; I added babel-register to compile a new ES standards to old ES5 (impors, object spread…). After that I configured enzyme adapter and axios client. And the most important part — jsdom configuration. #### .nycrc { "extension": \[".js",".jsx"\], "require": \["./tests/helper.js"\], "exclude": \[ "node\_modules", "build", "coverage", "tests", "src/registerServiceWorker.js" \], "check-coverage": true, "per-file": false, "statements": 80, "branches": 80, "functions": 80, "lines": 80, "reporter": \[ "lcov", "text", "text-summary", "html" \], "all": true } It’s nyc config. We use it for getting code coverage info. As you can see from config — we need to test js/jsx files, add our helper and exclude some directories and files from testing. Other parameters help with coverage reports. To run it add line to **package.json**: "scripts": { "start": "react-scripts start", "build": "react-scripts build", "eject": "react-scripts eject", "test": "npm-run-all test:\*", "test:security": "nsp check", "test:lint": "eslint . --ext .js,.jsx", **"test:coverage": "nyc mocha --timeout 5000 tests/\*\*/\*.{js,jsx}"** }, Here we run nyc with mocha, we set timeout for testing and path to test files. If you want to run only mocha without nyc: mocha --timeout 5000 --require tests/helper.js tests/\*\*/\*.{js,jsx} Now we can run testing: yarn test  We didn’t pass it because we don’t have any tests. Let’s create it… #### Action types (**tests/modules/post/types/index.js**): **import** { expect } **from** 'chai'; **import** \* **as** types **from** '../../../../src/modules/post/types'; describe('Testing post module types', () => { it('should test POST\_TITLE', () => { expect(types.POST\_TITLE).to.be.equal('@@post/TITLE'); }); it('should test POST\_BODY', () => { expect(types.POST\_BODY).to.be.equal('@@post/BODY'); }); it('should test POST\_SUBMIT', () => { expect(types.POST\_SUBMIT).to.be.equal('@@post/SUBMIT'); }); }); In each test I need to import **expect** tool from **chai** package. It helps to make assertation in BDD style. Each test should start from **describe** section and include **it** section. #### Action creators (**tests/modules/post/actions/index.js**): **import** { expect } **from** 'chai'; **import** \* **as** actions **from** '../../../../src/modules/post/actions'; **import** \* **as** types **from** '../../../../src/modules/post/types'; describe('Testing post module actions', () => { it('should test titleAction', () => { **const** title = 'title'; **const** action = actions.titleAction(title); expect(action).to.be.a('object'); expect(action.type).to.be.equal(types.POST\_TITLE); expect(action.title).to.be.equal(title); }); it('should test bodyAction', () => { **const** body = 'body'; **const** action = actions.bodyAction(body); expect(action).to.be.a('object'); expect(action.type).to.be.equal(types.POST\_BODY); expect(action.body).to.be.equal(body); }); it('should test submitAction', () => { **const** action = actions.submitAction(); expect(action).to.be.a('object'); expect(action.type).to.be.equal(types.POST\_SUBMIT); }); }); As you can see we work with pure functions. No side effects. All time the same results using the same parameters. Easy to test — just run it and check result. Action creators always return object with at least one property — action type. It’s required parameter for working with store and we should test it each time. #### Container (tests/modules/post/containers/index.jsx): **import** React **from** 'react'; **import** { mount } **from** 'enzyme'; **import** { expect } **from** 'chai'; **import** { spy } **from** 'sinon'; **import** configureMockStore **from** 'redux-mock-store'; **import** { Provider } **from** 'react-redux'; **import** PostContainer **from** '../../../../src/modules/post/containers'; describe('Testing post module containers', () => { it('should test containers with login', () => { **const** Component = spy(() => **null**); **const** Container = PostContainer(Component); **const** title = 'title'; **const** body = 'body'; **const** mockStore = configureMockStore(\[\]); **const** store = mockStore({ post: { title, body, }, }); **const** wrapper = mount(<Provider store={store}><Container /></Provider>); expect(wrapper.find(Component)).to.have.length(1); **const** props = wrapper.find(Component).props(); expect(props.title).to.be.equal(title); expect(props.body).to.be.equal(body); expect(props.titleAction).to.be.a('function'); expect(props.bodyAction).to.be.a('function'); expect(props.submitAction).to.be.a('function'); }); }); In this test we use **mount** because we need to work with storage. As redux is a singleton, to run each test we should clear it. Redux-mock-store helps with it. Next we create store, mount our container and check component props. We don’t need a real component, just use sinon **spy**. #### Component (**tests/modules/post/containers/index.jsx**): **import** React **from** 'react'; **import** { shallow } **from** 'enzyme'; **import** { expect } **from** 'chai'; **import** { spy } **from** 'sinon'; **import** PostComponent **from** '../../../../src/modules/post/components'; describe('Testing post module PostComponent', () => { **const** title = 'title'; **const** body = 'body'; **const** titleAction = () => 1; **const** bodyAction = () => 1; **const** submitAction = () => 1; **const** props = { title, body, titleAction, bodyAction, submitAction, }; it('should test PostComponent', () => { **const** wrapper = shallow(<PostComponent {...props} />); expect(wrapper.find('form')).to.have.length(1); expect(wrapper.find('h1')).to.have.length(1); expect(wrapper.find('h1').props().children).to.be.equal('Our form example'); expect(wrapper.find('div')).to.have.length(3); expect(wrapper.find('input')).to.have.length(2); **const** titleProps = wrapper.find('input').first().props(); expect(titleProps.type).to.be.equal('text'); expect(titleProps.value).to.be.equal(title); expect(titleProps.onChange).to.be.a('function'); expect(wrapper.find('textarea')).to.have.length(1); **const** bodyProps = wrapper.find('textarea').props(); expect(bodyProps.value).to.be.equal(body); expect(bodyProps.onChange).to.be.a('function'); **const** submitProps = wrapper.find('input').last().props(); expect(submitProps.type).to.be.equal('submit'); expect(submitProps.value).to.be.equal('Submit'); }); it('should test PostComponent titleAction', () => { **const** onChange = spy(); **const** wrapper = shallow(<PostComponent {...props} **titleAction**\={onChange} />); expect(wrapper.find('input')).to.have.length(2); **const** titleProps = wrapper.find('input').first().props(); expect(titleProps.type).to.be.equal('text'); expect(titleProps.value).to.be.equal(title); expect(titleProps.onChange).to.be.a('function'); wrapper.find('input').first().simulate('change', { target: { value: 'test' } }); expect(onChange.withArgs('test').calledOnce).to.be.equal(**true**); }); it('should test PostComponent bodyAction', () => { **const** onChange = spy(); **const** wrapper = shallow(<PostComponent {...props} **bodyAction**\={onChange} />); expect(wrapper.find('textarea')).to.have.length(1); **const** bodyProps = wrapper.find('textarea').props(); expect(bodyProps.value).to.be.equal(body); expect(bodyProps.onChange).to.be.a('function'); wrapper.find('textarea').simulate('change', { target: { value: 'test' } }); expect(onChange.withArgs('test').calledOnce).to.be.equal(**true**); }); it('should test PostComponent submitAction', () => { **const** onChange = spy(); **const** preventDefault = spy(); **const** wrapper = shallow(<PostComponent {...props} **submitAction**\={onChange} />); expect(wrapper.find('form')).to.have.length(1); wrapper.find('form').simulate('submit', { preventDefault }); expect(onChange.calledOnce).to.be.equal(**true**); expect(preventDefault.calledOnce).to.be.equal(**true**); }); }); To test each component (if it’s created as a pure function), we just need to make **shallow** render and test props. Some times we need to simulate DOM actions like **click** or **submit**. #### Entry point (tests/modules/post/index.jsx): **import** React **from** 'react'; **import** { mount } **from** 'enzyme'; **import** { expect } **from** 'chai'; **import** { spy } **from** 'sinon'; **import** configureMockStore **from** 'redux-mock-store'; **import** { Provider } **from** 'react-redux'; **import** PostModule **from** '../../../src/modules/post'; **import** PostComponent **from** '../../../src/modules/post/components'; describe('Testing post module', () => { it('should test post module', () => { **const** title = 'title'; **const** body = 'body'; **const** mockStore = configureMockStore(\[\]); **const** store = mockStore({ post: { title, body, }, }); **const** wrapper = mount(<Provider store={store}><PostModule /></Provider>); expect(wrapper.find(PostComponent)).to.have.length(1); **const** props = wrapper.find(PostComponent).props(); expect(props.title).to.be.equal(title); expect(props.body).to.be.equal(body); expect(props.titleAction).to.be.a('function'); expect(props.bodyAction).to.be.a('function'); expect(props.submitAction).to.be.a('function'); }); }); The same like container test. If we disable checking of all other code except our module: #### .nycrc { "extension": \[".js",".jsx"\], "require": \["./tests/helper.js"\], "exclude": \[ "node\_modules", "build", "coverage", "tests", "src/registerServiceWorker.js", **"src/\*.\*"** \], "check-coverage": true, "per-file": false, "statements": 80, "branches": 80, "functions": 80, "lines": 80, "reporter": \[ "lcov", "text", "text-summary", "html" \], "all": true } We have full code coverage:  So, as you see, we spend a lot of time to create and test redux stuff. To save time I created redux-lazy. I described it in [**previous part**](https://medium.com/@evheniybystrov/react-redux-for-lazy-developers-b551f16a456f). Now I want to delete all redux code and replace it by 7 lines of code. yarn add redux-lazy And add it to our module (**src/modules/post/rl/index.js**): **import** RL **from** 'redux-lazy'; **const** rl = **new** RL('post'); rl.addAction('title', { title: '' }); rl.addAction('body', { body: '' }); rl.addAction('submit'); **const** result = rl.flush(); **export default** result; Let’s update our actions and reducer: #### Action creators: **import** rl **from** '../rl'; **const { POST\_TITLE, POST\_BODY, POST\_SUBMIT, } = rl.types;** **export const** titleAction = title => ({ type: POST\_TITLE, title, }); **export const** bodyAction = body => ({ type: POST\_BODY, body, }); **export const** submitAction = () => ({ type: POST\_SUBMIT, }); #### Reducer: **import** rl **from** '../rl'; **const { POST\_TITLE, POST\_BODY, } = rl.types;** **const** defaultState = { title: '', body: '', }; **export default** (state = defaultState, action) => { **switch** (action.type) { **case** POST\_TITLE: **case** POST\_BODY: **return** { ...state, ...action }; **default**: **return** state; } }; #### Types test: **import** { expect } **from** 'chai'; **import** rl **from** '../../../../src/modules/post/rl'; **const** { types } = rl; describe('Testing post module types', () => { it('should test POST\_TITLE', () => { expect(types.POST\_TITLE).to.be.equal('@@post/TITLE'); }); it('should test POST\_BODY', () => { expect(types.POST\_BODY).to.be.equal('@@post/BODY'); }); it('should test POST\_SUBMIT', () => { expect(types.POST\_SUBMIT).to.be.equal('@@post/SUBMIT'); }); }); And after testing we see the same results. Let’s delete types file and run tests again. It’s ok. So we can combine at least creating types manual and redux-lazy. **Next step**: action creators. #### Reducer: **import** { connect } **from** 'react-redux'; **import** rl **from** '../rl'; **const { actions } = rl;** **const** mapStateToProps = state => state.post; **const** mapDispatchToProps = { ...actions }; **export default** connect(mapStateToProps, mapDispatchToProps); #### And tests: **import** { expect } **from** 'chai'; **import** rl **from** '../../../../src/modules/post/rl'; **const { types, actions } = rl;** describe('Testing post module actions', () => { it('should test titleAction', () => { **const** title = 'title'; **const** action = actions.titleAction(**{ title }**); expect(action).to.be.a('object'); expect(action.type).to.be.equal(types.POST\_TITLE); expect(**action.payload.title**).to.be.equal(title); }); it('should test bodyAction', () => { **const** body = 'body'; **const** action = actions.bodyAction(**{ body }**); expect(action).to.be.a('object'); expect(action.type).to.be.equal(types.POST\_BODY); expect(**action.payload.body**).to.be.equal(body); }); it('should test submitAction', () => { **const** action = actions.submitAction(); expect(action).to.be.a('object'); expect(action.type).to.be.equal(types.POST\_SUBMIT); }); }); **Next step**: reducer (**src/reducers.js**) **import** { combineReducers } **from** 'redux'; **import** rl **from** './modules/post/rl'; **const** { nameSpace: postNameSpace, reducer: postReducer } = rl; **export default** combineReducers({ \[postNameSpace\]: postReducer }); And we can delete it. And **last step** is container. If it’s so simple (just part of state from the module and all actions from the module), we can get it from redux-lazy too. Our entry point in the end: **import** PostComponent **from** './components'; **import** rl **from** './rl'; **const** { Container: PostContainer } = rl; **export default** PostContainer(PostComponent); If we run our app we see the same result:  And if we run tests:  We can see the same tests, the same functionality but less code. Let’s see tree:  You can open coverage report in your browser. Just open **coverage/index.html**   This article bigger then I planned. So I can give more examples (redux-observable, reselect, recompose) in next part. But the main goal of this article is to show how fast and easy to work with redux using redux-lazy. We make a lot of code lines to just get/set data to store. In first code coverage report we had 27 lines of code. Now we have 16. We have 59% less code with the same functionality. And again... Some developers put a lot of logic to reducer (change data brfore saving). But it’s hard to support. All code should be simple like pure function: **props => JSX** What could be easy? Keep pure your components, your store and move all logic to special tools like redux-observable and redux-lazy. [**React — redux for lazy developers. Part 3** _It’s the last part of series about creating react redux app._hackernoon.com](https://hackernoon.com/react-redux-for-lazy-developers-part-3-319b639a22c3 "https://hackernoon.com/react-redux-for-lazy-developers-part-3-319b639a22c3")[](https://hackernoon.com/react-redux-for-lazy-developers-part-3-319b639a22c3)