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: _Each time working with redux in react app we spend a lot of time to make action types, action creators, reducers… Most…_medium.com React — redux for lazy developers And _As you may know working with react you can use functions or classes — work with stateless and stateful components. In…_blog.cloudboost.io React: functional way As I use for side effects I put all logic to . React is just a view layer. All redux stuff I use only as getters/setters for store data and events for epics. redux-observable 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 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 , . After that install and like in . And create project: node.js npm yarn create-react-app previous article mkdir redux-appcd redux-appcreate-react-app . And run it: yarn start Result: Let’s install and : redux 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 service. And next we will create another module to work with comments. JSONPlaceholder : we should create directories and empty files. First step mkdir -p src/modules/postmkdir src/modules/post/componentsmkdir src/modules/post/containersmkdir src/modules/post/typesmkdir src/modules/post/actionsmkdir src/modules/post/reducermkdir src/modules/post/epics touch src/modules/post/index.jstouch src/modules/post/components/index.jsxtouch src/modules/post/containers/index.jstouch src/modules/post/types/index.jstouch src/modules/post/actions/index.jstouch src/modules/post/reducer/index.jstouch src/modules/post/epics/index.js All code you can find on . github And our tree: Don’t forget to install . It useful if you need to check current state: redux-logger yarn add redux-logger And — it helps to validate react component properties: prop-types yarn add prop-types : we need to create store and reducers. Next step Reducers: touch src/reducers.js With code: { combineReducers } 'redux'; import from post './modules/post/reducer'; import from combineReducers({ post }); export default And store: touch src/store.js With code: { createStore, applyMiddleware } 'redux'; logger 'redux-logger'; import from import from reducers './reducers'; import from store = createStore(reducers,applyMiddleware(logger)); const store; export default And update : src/index.js React 'react'; ReactDOM 'react-dom'; './index.css'; App './App'; registerServiceWorker './registerServiceWorker'; import from import from import { Provider } from 'react-redux'; import import from import store from './store'; import from ReactDOM.render( document.getElementById('root'));registerServiceWorker(); <Provider store={store}><App /></Provider>, 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): POST_TITLE = '@@post/TITLE'; POST_BODY = '@@post/BODY'; POST_SUBMIT = '@@post/SUBMIT'; export const export const export const To avoid collision I like to add prefix to types like /TITLE. @@post Action creators (src/modules/post/actions/index.js): {POST_TITLE,POST_BODY,POST_SUBMIT,} '../types'; import from titleAction = title => ({type: POST_TITLE,title,}); export const bodyAction = body => ({type: POST_BODY,body,}); export const submitAction = () => ({type: POST_SUBMIT,}); export const Reducer (src/modules/post/reducer/index.js): { POST_TITLE, POST_BODY } '../types'; import from defaultState = {title: '',body: '',}; const (state = defaultState, action) => { (action.type) { POST_TITLE: POST_BODY: { ...state, ...action }; : state;}}; export default switch case case return default return 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): { connect } 'react-redux'; * actions '../actions'; import from import as from mapStateToProps = state => state.post; mapDispatchToProps = { ...actions }; const const connect(mapStateToProps, mapDispatchToProps); export default 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 Component (src/modules/post/components/index.jsx): React 'react'; PropTypes 'prop-types'; import from import from PostComponent = props => (<form onSubmit={(event) => {event.preventDefault();props.submitAction();}}><h1>Our form example</h1><div><inputtype="text"onChange={event => props.titleAction(event.target.value)}value={props.title}/></div><div><textareaonChange={event => props.bodyAction(event.target.value)}value={props.body}/></div><div><input type="submit" value="Submit" /></div></form>); const PostComponent.propTypes = {title: PropTypes.string.isRequired,body: PropTypes.string.isRequired,titleAction: PropTypes.func.isRequired,bodyAction: PropTypes.func.isRequired,submitAction: PropTypes.func.isRequired,}; PostComponent; export default 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): PostComponent './components'; PostContainer './containers'; import from import from PostContainer(PostComponent); export default Last thing — we need to update to render our post module: src/App.jsx React, { Component } 'react'; logo './logo.svg'; './App.css'; import from import from import import Post from './modules/post'; App Component {render() { (<div className="App"><header className="App-header"><img src={logo} className="App-logo" alt="logo" /><h1 className="App-title">Welcome to React</h1></header> </div>);}} class extends return <Post /> App; export default 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: — a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun. mocha — a BDD / TDD assertion library for and the browser that can be delightfully paired with any javascript testing framework. chai node — standalone test spies, stubs and mocks for JavaScript. Works with any unit testing framework. sinon — a JavaScript Testing utility for React that makes it easier to assert, manipulate, and traverse your React Components’ output. enzyme — JavaScript test coverage tool. nyc 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 with and — node security platform command-line tool: eslint airbnb config nsp yarn add eslint eslint-config-airbnb eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react nsp jsdom redux-mock-store babel-register -D is a pure-JavaScript implementation of many web standards, notably the WHATWG DOM and HTML Standards, for use with Node.js. Jsdom — a mock store for your testing your redux async action creators and middleware. Redux-mock-store To run all commands I use — a CLI tool to run multiple npm-scripts in parallel or sequential: npm-run-all 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 buildcoveragesrc/registerServiceWorker.js .eslintrc {"parser": "babel-eslint","extends": "airbnb","env": {"browser": ,"node": ,"mocha": }} true true 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 Don’t forget about : 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 client. graphQL 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',],}); { configure } = require('enzyme'); Adapter = require('enzyme-adapter-react-16'); const const axios = require('axios'); httpAdapter = require('axios/lib/adapters/http'); const const axios.defaults.host = 'http://localhost:3000';axios.defaults.adapter = httpAdapter; configure({ adapter: Adapter() }); new { JSDOM } = require('jsdom'); const exposedProperties = ['window', 'navigator', 'document']; const { window } = JSDOM('', { url: 'http://localhost' });global.document = window.document;global.window = window;Object.keys(document.defaultView).forEach((property) => { ( global[property] === 'undefined') {exposedProperties.push(property);global[property] = document.defaultView[property];}}); const new if typeof 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 { expect } 'chai'; * types '../../../../src/modules/post/types'; import from import as from 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 tool from package. It helps to make assertation in BDD style. Each test should start from section and include section. expect chai describe it Action creators ( ): tests/modules/post/actions/index.js { expect } 'chai'; * actions '../../../../src/modules/post/actions'; * types '../../../../src/modules/post/types'; import from import as from import as from describe('Testing post module actions', () => {it('should test titleAction', () => { title = 'title'; const **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', () => { body = 'body'; const **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', () => { action = actions.submitAction(); const 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): React 'react'; { mount } 'enzyme'; { expect } 'chai'; { spy } 'sinon'; configureMockStore 'redux-mock-store'; { Provider } 'react-redux'; import from import from import from import from import from import from PostContainer '../../../../src/modules/post/containers'; import from describe('Testing post module containers', () => {it('should test containers with login', () => { Component = spy(() => ); Container = PostContainer(Component); const null const **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 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. mount 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 React 'react'; { shallow } 'enzyme'; { expect } 'chai'; { spy } 'sinon'; import from import from import from import from PostComponent '../../../../src/modules/post/components'; import from describe('Testing post module PostComponent', () => { title = 'title'; body = 'body'; titleAction = () => 1; bodyAction = () => 1; submitAction = () => 1; const const const const const props = {title,body,titleAction,bodyAction,submitAction,}; const it('should test PostComponent', () => { wrapper = shallow(<PostComponent {...props} />); const 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', () => { onChange = spy(); const **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', () => { onChange = spy(); const **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', () => { onChange = spy(); preventDefault = spy(); const const **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 render and test props. Some times we need to simulate DOM actions like or . shallow click submit Entry point (tests/modules/post/index.jsx): React 'react'; { mount } 'enzyme'; { expect } 'chai'; { spy } 'sinon'; configureMockStore 'redux-mock-store'; { Provider } 'react-redux'; import from import from import from import from import from import from PostModule '../../../src/modules/post'; PostComponent '../../../src/modules/post/components'; import from import from describe('Testing post module', () => {it('should test post module', () => { title = 'title'; body = 'body'; const const **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", ],"check-coverage": true,"per-file": false,"statements": 80,"branches": 80,"functions": 80,"lines": 80,"reporter": ["lcov","text","text-summary","html"],"all": true} "src/*.*" 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 . Now I want to delete all redux code and replace it by 7 lines of code. previous part yarn add redux-lazy And add it to our module ( ): src/modules/post/rl/index.js RL 'redux-lazy'; import from rl = RL('post');rl.addAction('title', { title: '' });rl.addAction('body', { body: '' });rl.addAction('submit'); const new result = rl.flush(); const result; export default Let’s update our actions and reducer: Action creators: rl '../rl'; import from const {POST_TITLE,POST_BODY,POST_SUBMIT,} = rl.types; titleAction = title => ({type: POST_TITLE,title,}); export const bodyAction = body => ({type: POST_BODY,body,}); export const submitAction = () => ({type: POST_SUBMIT,}); export const Reducer: rl '../rl'; import from const {POST_TITLE,POST_BODY,} = rl.types; defaultState = {title: '',body: '',}; const (state = defaultState, action) => { (action.type) { POST_TITLE: POST_BODY: { ...state, ...action }; : state;}}; export default switch case case return default return Types test: { expect } 'chai'; rl '../../../../src/modules/post/rl'; import from import from { types } = rl; const 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. : action creators. Next step Reducer: { connect } 'react-redux'; rl '../rl'; import from import from const { actions } = rl; mapStateToProps = state => state.post; mapDispatchToProps = { ...actions }; const const connect(mapStateToProps, mapDispatchToProps); export default And tests: { expect } 'chai'; rl '../../../../src/modules/post/rl'; import from import from const { types, actions } = rl; describe('Testing post module actions', () => {it('should test titleAction', () => { title = 'title'; const **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', () => { body = 'body'; const **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', () => { action = actions.submitAction(); const expect(action).to.be.a('object'); expect(action.type).to.be.equal(types.POST\_SUBMIT); });}); : reducer ( ) Next step src/reducers.js { combineReducers } 'redux'; import from rl './modules/post/rl'; import from { nameSpace: postNameSpace, reducer: postReducer } = rl; const combineReducers({ [postNameSpace]: postReducer }); export default And we can delete it. And 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. last step Our entry point in the end: PostComponent './components'; rl './rl'; import from import from { Container: PostContainer } = rl; const PostContainer(PostComponent); export default 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. _It’s the last part of series about creating react redux app._hackernoon.com React — redux for lazy developers. Part 3