ABC of Redux

June 09, 2018 0 Comments

ABC of Redux

 

 



Hello folks!

Lately, React and Redux have been the talk of the town and I've been playing around with it for a while and learnt a few nuances. I am writing this so anyone who's getting started with Redux can see it from beginner's perspective.

Redux

Redux has no dependency on react and vice-versa. They both work well together and do their respective roles.

React provides the view - Redux manages its state logic.

Redux Terminologies


  • store

  • state

  • dispatch

  • actions

  • action creators

  • reducers

Redux Lifecycle

Redux Lifecycle

The above picture gives a good overview of how redux cycle works.

Core Redux principles

  • It has a global state provider which is known as store which contains the entire state logic of your application.This has a huge advantage in the sense that there is a single source of truth for state and it is globally accessible throughout your application i.e. in all components once it is in redux store.

    For eg. We make an api call once and store the data in our redux store and then we can access the data in any of our components.I prefer using redux saga for making api call through redux but lets discuss that in some later article.

    State data can be anything from some checking a radio button to large data coming from some API.

  • Next question is we have a state but how do we update or add to it?
    Lets see how its done.

    A state should only be updated from dispatching an action through action creators (Remember the keywords I mentioned before) Lets see what they are :-

    Action is a simple javascript object which we dispatch or you can say launch in order to change a state. It will be better with an example. Lets say we have a label tag which has 'hello' written in it, we need to change it to 'bye' so how do we do it through redux. Our action will be something like this initially

  • { type: 'LABELVALUE', value: 'hello' } 

    And our react component will be something like

    const LabelComponent = () => ( <Label> { state.labelValue } </label>; 
    <Button onClick={changeLabel}> Click Me </Button>
    );

    Now we need to update its value on clicking a button so how do we do it?
    We dispatch an action on clicking of the button.


    Dispatching an action will be something like this :-

    const changeLabelOnClick = (newLabelValue) => { dispatch( changeLabelValue('bye') ); 
    } const LabelComponent = () => ( <Label> { state.labelValue } </label>;
    <Button onClick={changeLabelOnClick('bye')}> Click Me </Button>
    );

    Note here changeLabelValue is nothing but an action creator which returns a new object or an action. Here's how changeLabelValue will look:-

    const changeLabelValue = (labelValue) => { type: 'LABELVALUE', labelValue 
    }

    If you notice action creator is just a wrapper over an action which is a good practice. You can dispatch an action directly which would look something like this

     dispatch( { type: 'LABELVALUE', labelValue: 'Bye' } ); 

    Now next question is once you dispatch an action what really happens and how state changes ?

    Lets see :- As you guys would have noticed while dispatching an action object we added a 'type' key to it (type : 'LABELVALUE').This is a string which is the deciding factor as to which part of store will change and how it will change.


    Reducers are the ones which subscribe to these types and change the state accordingly. The actual change in state occurs in the reducers. Lets see how:-

    Reducer file snippet:-

    const labelReducer = (state = {}, action) => { switch(action.type){ case 'LABELVALUE':{ return action.labelValue; } default: return state; } 
    }

    This is a function in a reducer which returns a new state on getting called.

    This function is triggered when an action is dispatched with an action type which is present in this reducer. In this case the action type is 'LABEL
    VALUE'.

    If you notice it returns a new object and does not change an existing one.This is known as immutablity where we destory and create a new state everytime there is a change.

    Now if you have followed till now you will notice we can change the label value with any string we want. We just need to dispatch an action with appropriate value. For eg. dispatch( changeLabelValue('yipeee') ) and we are done, the value will be updated.

    Important thing to note here is the how label changed, we saw above :-

     <Label> {state.labelValue} </Label> 

    How this changes on dispatching an action? This is the beauty of react-redux.

    This Label is in a component - LabelComponent. Now this component uses 'labelValue' state of the store. Whenever there is a change in the redux with the labelValue this component will re render as it is subscribed to redux.So when this component is rerendered state.labelValue is already updated.

  • Now one important thing to note here is we have to bind view to state.

    All the above code snippets were shown in one file for understanding purpose but we need to separate view logic from container logic. We saw we had a LabelComponent before so it is a pure component as it just renders a view but it needs to have a separate container component which provides data or state data to this component from redux as props.

    Lets see both these files with full code:-

    Pure Component file

    //LabelComponent.js - Pure Component const LabelComponent = ({labelValue, changeLabelOnClick}) => ( <Label> { labelValue } </label>; 
    <Button onClick={changeLabelOnClick('bye')}> Click Me </Button>
    );

    Container file

    //LabelComponent.container.js import { connect } from 'react-redux'; 
    import { changeLabelValue} from '../../actions';
    import LabelComponent from './LabelComponent'; const mapStateToProps = (state) => { return { labelValue: state.labelValue }
    } const mapDispatchToProps = (dispatch) => { return { changeLabelOnClick: (labelValue) => dispatch(changeLabelValue(labelValue)) };
    }; export default connect(mapStateToProps, mapDispatchToProps)(LabelComponent);

    Lets understand these two files and how data flows from here to corresponding actions and reducers.

    Lets decode LabelComponent.container.js first :-
    Here we first import action creators we need in order to change state.


    After that you will notice two functions mapStateToProps and mapDispatchToProps These functions do very much what their name suggests.



    mapStateToProps takes data from redux and provides it to our view component which is LabelComponent in this case as you can see we use labelValue as prop in LabelComponent.

    Similar to this mapDispatchToProps provides functions as props to view component which can provide data back to containers as callbacks. Here in this case changeLabelOnClick is a callback function which is provided as a prop to LabelComponent. After this data is availabel in container we dispatch an action and data flows to reducer -> store and back to view with updated state. Now lets see LabelComponent.js

    Most of it is covered in previous file explanation. Here we first get the props from container ( we are using destructing in props ). Then I guess flow is pretty much clear. On click of button the labelValue flows back to container and the new existing labelValue is present in component as prop.

    Now these two files finally connect to each other through a very handy component -
    connectfrom 'react-redux'.We import LabelComponent in container and provide it with the state data as props by using connect module along with mapstateToProps and mapDispatchToProps and export it as a single component as you can see in file.

    export default connect(mapStateToProps, mapDispatchToProps)(LabelComponent); 

    One last thing I did not show how the entire store is available to the app and how the app subscribes to redux changes.I am attaching a small snippet to give the overview :-

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

    Consider this as the starting point of your react app which is rendered to Dom and has all child react components. Here we use certain very useful node_modules which help us in forming a bond between react and redux.You can check their detailed explanation in redux official documentation. Here createstore binds the entire state into a single store and assigns it to a variable. If you notice reducers is nothing but a folder with different reducer files having part of state.

    Provider is the other component to which we provide our entire store and it propogates the store to entire react app components and its children for it to b accessible.

    Now if all this still is a bit hazy thats all right. I am attaching a link to my github repo which has an end to end implementation of react with redux,

    React-Redux-App

    Hope it helps you. Feel free to drop a comment if you have any question.

    Kudos and have a nice and productive day :) !!


  • Tag cloud