DEV Community

Masoud Pezeshkzade
Masoud Pezeshkzade

Posted on • Edited on

State management in ReactJS using Redux library


There are several options for managing state inside ReactJs projects. You may have heard about libraries like Redux, Context API, Mobx and Unstated. In this article we are going to describe more about state management and Redux.

My story:
When I started working with React Native, as a native mobile developer, I found states scary and weird. In Java, we have Intents and Application class to send data between pages and store global variables. But none of them exists in React Native. Plus, In my first app, I didn't know why should we use a third-party to manage the application state while we have built-in state API. At that moment I just followed some tutorials about Redux and its implementation in React projects. Now after developing several applications with React and state managers, I found how state management can be useful to make our project structure better, project lines of code lower and your project more understandable for other developers. The thing is in big-scale projects, using state managers is a must!

I know that at first, maybe it will be wired and scary for you if you don't have a strong javascript understanding yet, but try to use it like me and don't blame yourself if you could not understand it deeply. Even today that I am writing this blog post, state management, and Redux is not easy peasy for me but I have to use it. I hope this article helps you to understand Redux better.

Redux
Redux is the first library that solved state management in React. It is so popular among React developers. In Redux we have several key elements:

1- Store: all our data will keep here.
2- Actions: they let us to send our data to Redux. Technically they are simple javascript objects that must have a type and can include several key-value data.

{
   type:"LOGIN_SUCCESS",
   token:"user token here ..."
}

We can also have action creators for ease:

function loginSuccess(token){
    return {
        type:"LOGIN_SUCCESS",
        token
    }
}
function logout(){
    return {
        type:"LOGOUT"
    }
}

3- Dispatch: it is a redux API that runs our actions. In fact, in order to save our data, we should run our actions using dispatch API.

store.dispatch(loginSuccess()); //dispatch api only accept javascript objects

4- Reducers: they are just pure javascript functions that accept actions as their argument and decide what to save in store when an action is sent to them.

const defaultState={
    auth: false,
    token: null
}

export default function users (state=defaultState, action){

    switch(action.type){

        case "LOGIN_SUCCESS":
            return {
                ...state,
                auth: true,
                token: action.token
            }
        case "LOGOUT":

            return {
                ...state,
                auth: false,
                token: null
            }

        default: 
            return state;
    }

}


** Keep in mind that you must not have any side effect operation inside your reducer function. For example, you cannot fetch data from your API inside your reducer function. If you are looking for more detail about PURE functions in javascript, please check this blog post.

** Writing only one general reducer? Redux accepts only one reducer but it is a bad practice to have a big general reducer function. It is better to write reducers based on their functionalities. For instance, we can have a reducer for users' stuff and one another for notifications and so on. At the end of the article, you see how we can use combineReducer API to merge all reducers to one.

Connecting your app to Redux
The next part is installing the Redux library and connecting your app to it.

npm install --save redux react-redux

After installing libraries open index.js file. At first, it should look like it:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();


We need to change it to:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {createStore,combineReducers} from 'redux' //1
import users from './redux/users'//2
import {Provider} from 'react-redux' //3

const reducers=combineReducers({notifications}) //4
const store=createStore(reducers); //5


ReactDOM.render(<Provider store={store}><App/></Provider>, document.getElementById('root')); {/* 6 */}

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

Line 1: importing createStore and combineReducers API's from redux package.
Line 2: importing our reducers. remember we have only one reducer but you can have as many as you need.
Line 3: importing Provider component. The provider component lets us connect our app to our store.
Line 4: combining all reducers to one reducer using combineReducer API. In the case of having several reducers we should write like this:


const reducers=combineReducers({users,notifications,test1,test2})

Line 5: Creating our store based on main reducer.
Line 6: Putting component inside .

Read and write state
Now everything is ready and we can read and write our states. You only need to connect your component to the Redux to complete your job.

import React from 'react';
import logo from './logo.svg';
import './App.css';
import { connect } from 'react-redux';
import {login,logout} from './redux/users';

class App extends React.Component{


  componentDidMount(){
    setTimeout(() => {
      this.props.login("token here...") //1
    }, 1000);

    setTimeout(() => {
      this.props.logout(); //2
    }, 5000);

  }
  render() {

    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            USER TOKEN :
          </p>

          {this.props.users.auth && <p>{this.props.users.token}</p>} {/* 3 */}

        </header>
      </div>
    );
  }
}

function mapStateToProps(state){
  return{
    users: state.users
  }
}

const mapDispatchToProps={login,logout}

export default connect(mapStateToProps,mapDispatchToProps)(App); //4

Line 1 & 2: here we are calling our actions. Redux will send our actions to dispatch API and then passes it to our reducer...

Line 3: reading state from redux. Our component will be re-rendered when the user data changes.
Line 4: connecting our App component and then export it.

Github repository of this tutorial
Developers like to learn things through code examples. If you are looking for a working demo, you can check out my React Redux Github branch here. It is a basic react application that I implemented Redux in it. The reason why I made this repository is that it's gonna remind me implementation of all the useful libraries in Redux instead of looking on the internet for it. That is why I name it React Reminder. You can use this repo or you can have yours.

Summery
Redux is one of the useful libraries to manage your React application state. There are other options for you to do state management in your application like Context API. It is worth to take a look at other options before using Redux. In this article, we talked about how important state management is and showed how you can implement one of them in your projects step by step.
For more information about Redux, you can read its official documentation. If you are curious to learn more about Redux, I recommend you take a look at it's middlewares like Redux-Thunk and
specially Redux-Saga.

Top comments (0)