1

Adding redux in React project (Refactor a simple project with Redux)

Consider a simple project, a counter application that works with two buttons, once for Increment and another for Decrement counter value.

In an actual scenario, we use the state for holding counter value like this:

in App.js:

import React, {Component} from 'react';
import CounterApp from './CounterApp'

class App extends Component {
  render() {
    return (
      <CounterApp/>
    );
  }
}

export default App;

in CounterApp.js:

import React, {Component} from 'react';

class CounterApp extends Component {
  state = {
    counter: 0
  };

  handleIncrement = () => {
    this.setState(prevState => ({
      counter: prevState.counter + 1
    }))
  };

  handleDecrement = () => {
    this.setState(prevState => ({
      counter: prevState.counter - 1
    }))
  };

  render() {
    return (
      <div>
        <button onClick={this.handleIncrement}>Increment</button>
        <p>{this.state.counter}</p>
        <button onClick={this.handleDecrement}>Decrement</button>
      </div>
    );
  }
}

export default CounterApp;

A simple and basic example that implement with react class component and handled by two function handler (handleIncrement and handleDecrement)

And a state with a value, counter

I'm using prevState because of it's a best practice when you forced to use this.state. in setState!

Now, what would be this implementation with Redux?

3 Answers 3

9

First of all, you need to install redux and react-redux packages to your project via npm or yarn.

You can simply install them with one line of code:

npm install redux react-redux --save

or with yarn:

yarn add redux react-redux

now back to project and create 3 files with these names:

action.js, reducer.js and store.js

open action.js file. We should implement two actions for this app. One for increment and one for decrement.

in action.js

const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
const DECREMENT_COUNTER = 'DECREMENT_COUNTER';

const increment = () => ({type: INCREMENT_COUNTER});
const decrement = () => ({type: DECREMENT_COUNTER});

export {
  INCREMENT_COUNTER,
  DECREMENT_COUNTER,
  increment,
  decrement
}

actions are simple functions that dispatched from component to redux for changing the store(state) via reducers.

so we should change reducer.js:

import {INCREMENT_COUNTER, DECREMENT_COUNTER} from "./action";

const initialState = {
  counter: 0
};

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case(INCREMENT_COUNTER):
      return {
        ...state,
        counter: state.counter + 1
      };
    case (DECREMENT_COUNTER):
      return {
        ...state,
        counter: state.counter - 1
      };
    default:
      return state
  }
};

export default reducer

There are 3 main principles of using redux:

1- Single source of truth. The state of your whole application is stored in an object tree within a single store.

2- The state is read-only. The only way to change the state is to emit an action, an object describing what happened.

3- Changes are made with pure functions.

according to second principles, we must assume that the state is immutable, and each case(in switch) must return state individually. using ...state in the returned state means that if initialState will changing in future, these cases will work properly (in this example it's not necessary).

our functions in actions are pure(3rd principle)

and for last new file store.js:

import {createStore} from "redux";
import reducer from './reducer'

const store = createStore(reducer);

export default store;

now we should apply this store to our App component. so open App.js file and made these changes:

in App.js:

import React, {Component} from 'react';
import CounterApp from './CounterApp'
import {Provider} from 'react-redux'
import store from './store'

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <CounterApp/>
      </Provider>
    );
  }
}

export default App;

Provider wrapped the CounterApp component and will propagate store to App and CounterApp and all other child components.

finally, change the CounterApp.js:

import React, {Component} from 'react';
import {connect} from "react-redux";
import {increment, decrement} from "./action";

class CounterApp extends Component {

  handleIncrement = () => this.props.dispatch(increment());

  handleDecrement = () => this.props.dispatch(decrement());

  render() {
    return (
      <div>
        <button onClick={this.handleIncrement}>Increment</button>
        <p>{this.props.counter}</p>
        <button onClick={this.handleDecrement}>Decrement</button>
      </div>
    );
  }
}

const mapStateToProps = state => {
  const counter = state.counter;
  return {counter}
};

export default connect(mapStateToProps)(CounterApp);

we are using increment & decrement actions to dispatch actions to redux.

the state was removed and instead of state we create a special function mapStateToProps' and useconnect` to connect the state to component props.

That's done!

Sign up to request clarification or add additional context in comments.

1 Comment

There are many redux implementations, but this is a very simple procedure for it. for example, using mapDispatchToProps and so on ...
0

You need to first install the package for a step-by-step understanding of react-redux in the React application. Redux works in four concepts:

  • View
  • Action
  • Reducer
  • Store

Then create the separate files for Action, Reducers (multiple files if you've more than one reducers, then combine all of them into a single reducer), and finally the store where our application's global state exists.

I have written a comprehensive article on this topic. You can check it out at https://multiclick.pk/how-to-use-redux-in-react/

Happy Coding! 😍

Comments

-1

If you need to use Global State in your project, you also can use a better and easier solution called Master-Hook

First step: Instalation:

npm i master-hook.

Redux , react-redux , redux-thunk , reselect are already installed in the library and you need to follow the steps.

Second step: Create ‘src/hooks.js’ file

import MasterHook from 'master-hook'

export const useMyHook = MasterHook({
  storage: "myStorage",
  initialState: {
    myName: 'Vanda',
  },
  cache: {
    myName: 10000,
  }
})
  1. You create your component and export it (useMyHook)
  2. Set the initial State (initialState:...)
  3. Set how long the value need has to stay cached in ms (cache:...)

Step 3: Add Provider to src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import MasterHook from 'master-hook';

ReactDOM.render(
  <React.StrictMode>
    <MasterHook.Provider>
      <App />
    </MasterHook.Provider>
  </React.StrictMode>,
  document.getElementById('root')
);
  1. Import MasterHook
  2. Wrapp your file with MasterHook.Provider

Step 4 Use your hook in src/App.js

import logo from './logo.svg';
import './App.css';
import { useMyHook } from './hooks'

function App() {
  const { myName, setMyName } = useMyHook()

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          My name is {myName}
        </p>
        <a
          onClick={() => setMyName('Boris')}
          className="App-link"
        >
          Set my name to 'Boris'
        </a>
      </header>
    </div>
  );
}

export default App;
  1. Import your hook

useMyHook

  1. Declare your hook

const { myName, setMyName } = useMyHook()

  1. Use it in your code

{myName}

and

{()=>setMyName('')}

Delete href attribute to prevent it from changing the page. setMyName action is created automatically.

No need to connect to the store. It’s already connected.

Step 5 Start your project and enjoy! (npm run start)

You are connected to Redux. myName from myStorage is cached for 10 seconds. You can click the link, reload the page and make sure it is.

1 Comment

This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.