User Tools

Site Tools


Writing /app/www/public/data/meta/development/applications/reactjsredux.meta failed
development:applications:reactjsredux

React and Redux

Colm Carew 2017/08/11 03:35 Note I do not go into detail about npm or node or webpack or how to install packages. This is purely how to use React with Redux

NOTE decorators(@) do not work with create-react-app-tool - the reason behind this is that you need to enable the plugin via webpack. I give the smart component both with and without the Decorator

What is Redux?

Redux is a state container for JavaScript Apps. If you have read the React Intro you will know state is what controls the data in a component and how the component rerenders in React.

Redux manages the state/data of the frontend and it works very well with React.

As a source to watch there are 7 videos explaining how to use React with Redux if you would like a more in depth tutorial : https://www.youtube.com/watch?v=1w-oQ-i1XB8

Why do we need Redux?

My best advice on why we need Redux is build a full application without it. The benefit of that is you will learn a lot about react and how to pass components and data around and pass properties of a parent component down to children component and execute parent functions within children and getting the child of one element to rerender the parent etc.

All of which is very good to know and to know how to do.

The issue you WILL encounter eventually is that you will need to pass data down many many levels to a super great grandchild(not an actual component term :P) component even though all the components inbetween do not need to know about this data. Doing this tightly couples a lot of components together and makes them a lot less reusable as well as over completes your components such that you may have a component that needs like 10+ properties just to work or a developer just can't follow why the data is being passed from this component to this component since they have nothing to do with that specific set of data.

This is where Redux comes in.

The package.json

Before starting this you need a few dependencies so ensure your package.json looks something like :

{
  "name": "react-redux-starter",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^15.6.1",
    "react-dom": "^15.6.1",
    "react-redux": "^5.0.6",
    "react-scripts": "1.0.11",
    "redux": "^3.7.2",
    "redux-logger": "^2.10.2",
    "redux-promise-middleware": "^4.3.0",
    "redux-thunk": "^2.2.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

Redux

From here on out any component that uses Redux will be called a smart component and anything that does not will be a standard component/dumb component.

Think of the redux approach as like One big store (a JavaScript Object) that anything that uses redux has access to. This is a large over simplification but it does allow for some very and clean state management. Redux when done properly also allows for history so you are able to see what was in the store before an after a change happens.

The main items you want to be concerned with as regard Redux with React are :

  • actions
  • reducers
  • the store

Actions

Actions are the functions you wish to perform when something happens in the UI i.e. application is loaded, button is pressed, text area is populated or altered etc. Actions are what you want to call when an event happens so you take action on the event.

Actions like any other functions can take parameters but the actions should always return a JavaScript Object (or a function that dispatches a JS Object). This JavaScript object must have a member called 'type' where the type is the actions you wish to perform and it should have a payload which can be an object of whatever you want in it. Note it does not have to be called payload its entirely down to you but convention. just call it payload. Again the Object must have a type property!

Reducers

Reducers are functions which must return a state (the object contained within the store). A Point to note here is in the store you will have an entry for each reducer such that if you have 3 reducers called 'company', 'client' and 'account' then your store would look like

{
  company : {},
  client : {},
  account : {}
}

The reducer should cater for each scenario you send out with an action so usually you pull the type from the incoming state and use this (usually with a case statement - could use a Command Pattern if very large number of types) to determine what to do with the incoming payload i.e. you may have a type of CLIENT_FETCH_FULFILLED and you want to set the state.client to be action.payload (all of this should hopefully make more sense when we have some code examples below).

For every action file you have you should have a reducer such that if you have 3 files clientActions.js, accountActions.js and companyActions.js then you should also have clientReducer.js, accountReducer.js and companyReducer.js

The Store

The is where the whole state of the application is stored. The store uses the concept of Middleware, I will not be going into heavy detail on this as the tutorial linked above will give a better explanation but middleware is how the results of the actions get communicated to the reducers and thus alter the state/store.

In most cases (depending on how you want it configured of course) a store.js would look like :

import {applyMiddleware, createStore} from 'redux';
 
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import promise from 'redux-promise-middleware';
 
import reducer from './reducers';
 
const middleware = applyMiddleware(promise(), thunk, logger());
export default createStore(reducer, middleware);

Note the line

import reducer from './reducers';

in this case there is an index.js file in a reducers folder that brings together all of the reducers.

Setup and Example

Now to be honest, overall this is complicated to setup, but once setup it runs very nicely and is easy enough to add on to. Just remember when there store gets updated this basically the same as calling .setState in all the components, the Virtual DOM then compares the differences and renders them.

Overall you would want your full React + Redux project structure to look something like :

The Contents of the store.js is as in 'The Store' section.

index.js

The index.js should contain something like :

import React from "react";
import ReactDOM from "react-dom";
import {Provider} from "react-redux";
import App from "./components/App";
import store from "./store";
 
const rootElement = document.getElementById('root');
 
ReactDOM.render(<Provider store={store}>
                 <App />
                </Provider>, rootElement);

The main difference between this index.js and the one in the React only tutorial is the additions of the Provider from react-redux which wraps the render of App and the store which is used as a prop for the Provider.

accountActions.js

Now lets make our first action. We will go with the accountActions.js and pretend we are still working with a counter as in the last app just so you can compare the difference between a smart and a dumb component.

export function incrementCounter(incrementBy) {
    return {
        type: "INCREMENT_COUNTER",
        payload: incrementBy
    };
}

accountReducer.js

The reducer is where you set the default state of that value in the store so this will look like :

export default function reducer(state = {
    count: 1,
    anyOtherValueOrObject : {}
}, action) {
    // This is what allows the history by creating a new state that is a clone of the current state
    // before modification
    state = Object.assign({}, state);
    switch (action.type) {
        case "INCREMENT_COUNTER" : {
            let counterValue = state.count;
            counterValue += action.payload
            state.count = counterValue;
            break;
        }
    }
    return state;
}

./reducers/index.js

This is where you want to combine all of your reducers together :

import {combineReducers} from 'redux';
import account from './accountReducer';
import client from './clientReducer';
import company from './companyReducer';
 
export default combineReducers({
    account,
    client,
    company
});

Make sure your reducers are all coded as you want them, if you are not ready to test a reducer yet just remove it from the combineReducers method. These reducers are then used in with the store so the middleware knows the reducers.

We should now be ready to start making smart component - Note You should try have as few smart components as you can as if everything is a smart component then none of them are. If you can pass data down a level or two and that child isn't doing anything crazy then it probably does not need to be a smart component.

App.js - Smart Version

Use Decorators - So using Webpack

import React, {Component} from 'react';
import {connect} from "react-redux";
import * as accountActions from '../actions/accountActions';
 
 
@connect((store) => {
    return {
        count: store.account.count
    };
}) 
class App extends Component {
    componentDidMount(){
        this.incresaseCounter();
    }
 
    incresaseCounter() {
        this.props.dispatch(accountActions.incrementCounter(1));
    }
 
    render() {
        return (
            <div>
              <h2>Count : {this.props.count}</h2>
              <button type="button" className="btn" onClick={this.incresaseCounter.bind(this)}>Basic</button>
            </div>
        );
    }
}
export default App;

Without using Decorators - create-react-app

import React, {Component} from 'react';
import {connect} from "react-redux";
import * as accountActions from '../actions/accountActions';
 
 
class App extends Component {
    componentDidMount(){
        this.incresaseCounter();
    }
 
    incresaseCounter() {
        this.props.dispatch(accountActions.incrementCounter(1));
    }
 
    render() {
        return (
            <div>
              <h2>Count : {this.props.count}</h2>
              <button type="button" className="btn" onClick={this.incresaseCounter.bind(this)}>Basic</button>
            </div>
        );
    }
}
 
function mapStateToProps(store) {
  return { count: store.account.count };
}
 
export default connect(mapStateToProps)(App);

That should be everything you require. If you're getting issues with the reducer index.js remember to remove the unused reducers.

The example of this project without any npm or webpack can be found in Dropbox/Errigal Shared Documents/Training/React_Redux_Example.zip

Solution

For a sample solution please go to : Dropbox/Errigal Shared Documents/Training/react-redux-starter.zip - this solution is the create-react-app solution so it does not use the decorator

development/applications/reactjsredux.txt · Last modified: 2021/06/25 10:09 by 127.0.0.1