Monsieur Zartosht

Monsieur Zartosht

Who am I?

A very simple blog that belongs to a very simple full-stack web developer about his daily life and struggles through coding. here you will find many things such as music, movies, and code (lots of codes!)


What I write about


Recent Posts

Why React is better with Redux?

Coding with React is fun and all but when your application grows bigger and bigger it is cumbersome to track all state changes in so many places and have all components refresh with every tiny single change in the state! Then what can we do? you can use Redux.

Table of contents

    In my previous post you saw that using React for developing front-end is so much easy and fun! but what about when your app grows bigger, when there are too many parts that are updating, fading, adding or removing from view simultaneously? I've been there it's horrible!

    What is Redux

    Redux is a predictable state container for JavaScript apps.

    It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time-traveling debugger.

    You can use Redux together with React, or with any other view library. It is tiny (2kB, including dependencies), but has a large ecosystem of addons available.

    Redux Website

    In a simpler more user friendly manner, Instead of every single components have their own states and re-rendering every child components with changes that happened in parent component, all states will be saved in one single object that called store, and all changes in that state is handled by dispatch method, and each component only re-render if it is dependent on that part of state! Pretty simple? No?

    What is store?

    Store is a single object that holds all of your component's states in itself! you can change the state by calling dispatch method, subscribe to some changes in state by listen method or remove listeners by calling the method that returned by subscribe method.

    How to make a project with React-Redux

    In the following, I will tell you what I do every time I'm going to create a new React project. first, init the npm project by:

    npm init

    set package name, version, description, entry point, test command, git repository, keywords, author, license and at last confirm the info by pressing Enter key. Remember you could leave all the fields blank for your projects. Now you have a package.json file in the root folder of your project with the following contents:

    {
      "name": "tutorial",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "Zartosht Sepideman",
      "license": "ISC"
    }

    Remember me to make another post to show how making react projects with laravel-mix!

    Now, Install react, react-dom, redux, react-redux, redux-thunk, redux-logger:

    npm install react redux react-redux redux-thunk

    redux-thunk is a middleware for making async actions, like fetching data from API or do some calculations.

    npm install redux-logger --dev

    redux-logger is a middleware for logging every state changes in the app, make it as dev-dependency to only be available in development builds.

    by running these commands, now we can set sail to make or React project awesome!

    Let's dive deeper

    In Redux, every dispatch returns a simple JSON object that has the property type for distinguishing dispatches from one another and another optional field as data.

    {
        type: MY_CUSTOM_TYPE,
        data
    }

    For being a good programmer we make functions that they return this JSON object. This functions called actions and personally I put them inside a folder named actions and in a file named according to their area of functionality (userActions.js, postActions.js, shopActions.js).

    //someFunctionalityActions.js
    ...
    export function changeSomethingAction(someOptionalData) {
        return {
            type: MY_CUSTOM_TYPE,
            someOptionalData
        }
    }
    
    export function loadingAction(loading) {
        return {
            type: LOADING,
            loading
        }
    }
    ...

    In the JSON object that returned from action there is a property called type, this is just some string value. You can just type it as a string or be a good programmer and make constants that are equal to that string and use those constants as your action types. I make a file named types.js and put it inside that actions folder that you created earlier.

    //types.js
    ...
    export const MY_CUSTOM_TYPE = "MY_CUSTOM_TYPE";
    export const LOADING = "LOADING";
    ...

    Now, for dispatching an action all you have to do is call:

    dispatch(changeSomethingAction(someData))
    dispatch(loadingAction(true))

    But wait I can't remember we handled dispatching of actions or event creating a store, Do you? 😃

    Make another file named -mainly I name this file as my application name- tutorialApp.js. This file is our reducer! but don't afraid of its name, it is just function that returns a new state depending on which action dispatched! a reducer consist of a switch that depending on action types return a new state.

    It's a good idea to make a JSON object inside reducer that called initialState that holds all the default values for your applications. your reducer looks like this:

    //tutorialApp.js
    
    export const initialState = {
        loading: false, // some example default data
        someData: []
    }
    
    export function tutorialApp(state = initailState, action) {
        switch(action.type) {
            case LOADING:
                return Object({}, state, {
                    loading: action.loading
                });
            case MY_CUSTOM_TYPE:
                return Object({}, state, {
                    someData: action.someOptionalData
                });
            default:
                return state;
        }
    }

    Always return a new JSON object from reducer when changing the application state instead of returning the changed one.

    The mighty Redux store

    I told you that all of the application's states are safe within store, now, we are going to create that store. make a file named store.js and inside that put the following contents:

    import tutorialApp                      from "./reducer";
    import { applyMiddleware, createStore } from "redux";
    import thunkMiddleware                  from "redux-thunk";
    import { createLogger }                 from "redux-logger";
    
    const loggerMiddleware = createLogger();
    
    let store = createStore(
        tutorialPlayer,
        applyMiddleware(
            thunkMiddleware,
            loggerMiddleware
        )
    );
    
    export default store;

    That's it! with 13 lines of code you created something that you could call it database for your web application!

    How to use Redux with React

    Now that you come this far, maybe you are wondering how the hell I connect these with my react app!! It is very simple, instead of that general App.js make a file named Provider.js. This Provider make sure that all Components and their children have access to that store.

    // Provider.js
    
    import App          from "./App";
    import React        from "react";
    import ReactDOM     from "react-dom";
    import { Provider } from "react-redux";
    import store        from "./store";
    
    ReactDOM.render(<Provider store={store}><App/></Provider>, document.getElementById("app"));

    and inside App.js (or every other Component that you are going to create):

    // App.js
    
    import React, { Component } from "react";
    import { connect }          from "react-redux";
    import * as actions         from "./actions";
    
    class App extends Component {
        render(){
            return this.props.loading ? <p>Loading</p> : <p>Not loading</p>
        }
    }
    
    const mapStateToProps = (state) => {
        return {
            loading: state.loading,
            someData: state.someData
        };
    };
    
    export default connect(mapStateToProps)(App);

    Making an initialState is for making sure codes like above App.js won't throw an error that loading is not defined!

    You are wondering what is going on here? then continue on reading.

    When we wrapped our App component inside that Provider, we also made a function named connect() available to all of its children!

    Connect method

    Connect method accepts two arguments that both are nullable, the first one is mapStateToProps and the other is mapDispatchToProps. I always ignore the second one, if you do this, you have a prop named dispatch available for dispatching actions like this:

    // App.js or every other component
    ...
        render(){
            return this.props.loading ? <p onClick={(event) => {event.preventDefault(); this.props.dispatch(loadingAction(false))}}>Loading</p> : <p onClick={(event) => {event.preventDefault(); this.props.dispatch(loadingAction(true))}}>Not loading</p>
        }
    ...

    or in a better world:

    // App.js or every other component
    ...
        startLoading(event) {
            event.preventDefault();
            this.props.dispatch(loadingAction(true));
        }
    
        finishLoading(event) {
            event.preventDefault();
            this.props.dispatch(loadingAction(false));
        }
    
        render(){
            return this.props.loading ? <p onClick={this.finishLoading.bind(this)}>Loading</p> : <p onClick={this.startLoading.bind(this)}>Not loading</p>
        }
    ...

    or if you want to have one method and pass another param to the event handler as well to the following:

    // App.js or every other component
    ...
        loadingEventHandler(event, state) {
            event.preventDefault();
            this.props.dispatch(loadingAction(state));
        }
    
        render(){
            return this.props.loading ? <p onClick={(event) => this.loadingEventHandler(event, false)}>Loading</p> : <p onClick={(event) => this.loadingEventHandler(event, true)}>Not loading</p>
        }
    ...

    mapStateToProps constant

    This constant makes the Component dependent on the returned states, so if those states changed, that Component will be re-rendered.

    That's it! now you have a React-Redux app! When I first encountered lots and lots of codes that were very complicated to comprehend!

    I hope it helped!


    Posted in Front-end , Javascript , React , Redux , Web on by

    Share this post: