Learn redux with todo-list project | React-redux for beginners

 Flow chart for react-redux - 



Understanding how redux work first- 

Redux has (always) a single store.

  1. Whenever you want to replace the state in the store, you dispatch an action.

  2. The action is caught by one or more reducers.

  3. The reducer/s create a new state that combines the old state, and the dispatched action.

  4. The store subscribers are notified that there is a new state.


Step 0 - Install redux packages

→ npm i react-redux redux redux-thunk

Step 1 - Creating a redux store

→ create a store.js file in src/app/store.js.

1st way : 

src/store.js

src/store.js
import { createStore } from 'redux'
export default configureStore({  reducer: { },})


2nd Way (using combineReducer) - recommended

combineReducers - It combines all the reducers into a single state

import { createStorecombineReducersfrom 'redux';

const reducer = combineReducers({
    //it will containe all our reducers
})

const store = createStore(reducer)

export default store


Step 2 -  Making redux store available to the entire application

→ Include a  <Provider /> component, which makes the Redux store available to the rest of your app:

index.js : 

---------------

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { Provider } from "react-redux";
import store from './store'

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



Step 2.5 - Installing and setting up redux dev tool chrom extension

→ Install chrome extension  - Redux dev tools

→ Install dev tool extension to your project - npm install --save redux-devtools-extension

import { createStorecombineReducers  } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';

const reducer = combineReducers({
    //it will containe all our reducers
})

const store = createStore(
    reducer,
    composeWithDevTools(///add any store enhancer like redux-thunk,etc here)   
)
export default store

→ composeWIthDevTools basically wraps any middleware or store enhancer like redux-thunk, etc

What is Middleware? 

Redux middleware provides a third-party extension point between dispatching an action, and the moment it reaches the reducer. People use Redux middleware for logging, crash reporting, talking to an asynchronous API, routing, and more

[Source - Google]

Step 3 : Installing a middleware to enhance our store

→ npm install redux-thunk

→ importing middleware to our store.js 

import { createStore, combineReducers, applyMiddleware} from 'redux';

→ add middleware to the store

const store = createStore(
        reducer,
        composeWithDevTools(applyMiddleware(...middleware)))

Final code - 

✔ store created

✔ middleware added

✔ combineRedcuer added to aggreagte multiple reducers

 ✔ chrome extension added to check reducer working 

store.js

------------

import { createStorecombineReducersapplyMiddlewarefrom 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';

const reducer = combineReducers({
    //it will contain all our reducers
})

const middleware = [thunk]

const store = createStore(
    reducer,
    composeWithDevTools(applyMiddleware(...middleware)))

export default store


Step 4: Creating Reducers 

→ create a folder "reducers" , then create a file "TodoReducers.js" = src/reducers/TodoReducers

 What will the reducer do?
/*
-> Action ka jo parameter hai wo reducer leta hai taaki store ko update kar sake
-> Action ke andar 2 parameter rehta hai
const exampleAction = {
    type : "ADD_TODO", --> helps to judge which switch case i.e action needs to be execute
    payload : "Learn redux" --> passes data to update the store (optional)
}
*/

TodoReducers.js 
-----------------------


const TodoReducers = (state = {todos : [] }, action=> {
    switch(action.type){
        case "ADD_TODO" :
            return { todos : action.payload}

        case "REMOVE_TODO":
            return { todos : action.payload}

        default :
        return state
    }

→ Assigning reducers to our store 

import { TodoReducers } from './reducers/TodoReducers';

const reducer = combineReducers({
    Todo : TodoReducers
})

Final Code - 

store.js:

---------

import { createStorecombineReducersapplyMiddlewarefrom 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import { TodoReducers } from './reducers/TodoReducers';

const reducer = combineReducers({
    Todo : TodoReducers
})

const middleware = [thunk]

const store = createStore(
        reducer,
        composeWithDevTools(applyMiddleware(...middleware)))

export default store


Step 5: Creating Action 

→ Create - src/actions/TodoActions.js

→ What will action do?

Whenever any action/activity is performed on the application, action is the place where the logic behind it is performed

The action takes two things - 

1. dispatch - this is the actual action which takes two arguments - 

dispatch({
      type: "ADD_TODO",
      payload: [{ id: todotodo }, ...todos],
    });

type - it helps reducer to finding which action is called

payload - the actual logic is written when the action is done on application

2. getState - helps to call the global state, so that it can be updated and in turn passed to reducers


→  In our to-do list, we have 2 actions - add data and remove the data.

Keeping that in mind, the actual code

TodoActions.js-

-------------------

//getState helps us to access our app state
//dispatch is the action which passes two agruments action and payload to the reducer

export const AddTodoAction = (todo=> (dispatchgetState=> {

  console.log(todo//this todo is coming from App.js which user is typing on input box
    
  const { Todo: { todos }} = getState(); //this the the gloabla state of our application

  console.log(todos)

  const hasTodo = todos.find((i=> i.todo === todo); //return boolean

  if (!hasTodo && todo !== "") {
    dispatch({
      type: "ADD_TODO",
      payload: [{ id: todotodo }, ...todos],
    });
  }
};

export const RemoveTodoAction = (todo=> (dispatchgetState=> {

  console.log("remove todo in todoaction folder")
  const {
    Todo: { todos },
  } = getState();


  dispatch({
    type: "REMOVE_TODO",
    payload: todos.filter((t=> t.id !== todo.id),
  });
};


Step 6 - Adding the action to our main application 

//useDispatch is used to call the action from our application

//useSelector is used to access the global state to our application


App.js:

----------

import "./App.css";
import { TiDelete } from "react-icons/ti";
import { useState } from "react";
import { useDispatchuseSelector } from "react-redux";
import { AddTodoActionRemoveTodoAction } from "./actions/TodoActions";

//useDispatch is use to call the action from our application
//useSelector is use to access global state to our application

function App() {
  const [todosetTodo] = useState();
  const dispatch = useDispatch();

  const Todo = useSelector((state=> state.Todo);

  const { todos } = Todo; //Global Todo

  const handleSubmit = (e=> {
    e.preventDefault();
    dispatch(AddTodoAction(todo));
  };

  return (
    <div className="App">
      <div className="App-header">
        <h2> To-do List (using react-redux) </h2>
        <form onSubmit={handleSubmit}>
          <input
            type="text"
            className="enter-todo"
            onChange={(e=> setTodo(e.target.value)}
          />
          <button type="submit" className="btn">
            Add
          </button>
        </form>
        {todos &&
          todos.map((eachTodo=> {
            if (eachTodo.length <= 1) {
              return <span>Cant enter blank todo</span>;
            } else {
              return (
                <ul className="allTodos">
                  <li key={eachTodo.id} className="singleTodo">
                    <span fontSize="50px">{eachTodo.todo}</span>
                    <TiDelete
                      color="white"
                      fontSize="30px"
                      style={paddingLeft: 10 }}
                      onClick={() => dispatch(RemoveTodoAction(eachTodo))}
                    />
                  </li>
                </ul>
              );
            }
          })}
      </div>
    </div>
  );
}

export default App;


Step 7 - If you liked it, comment below and follow me on insta - @methimanshu




Some Doubts : 

Q > Difference between redux/react-redux/redux-thunk/any others ?

  • redux - flux like flow with a single store, that can be used in whatever environment you like including vanilla js, react, angular 1/2, etc...

  • react-redux - bindings between redux and react. The library offers a set of react hooks - useSelector(), and useStore() to get the data from the store, and useDispatch() to dispatch actions. You can also use the connect() function to create HoCs (higher order components), that listen to the store's state changes, prepare the props for the wrapped component, and re-render the wrapped components when the state changes.

  • redux-thunk - middleware that allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. Used mainly for async calls to api, that dispatch another action on success / failure.

inshort

  1. redux: main library (independent from React)
  2. redux-thunk: a redux middleware which helps you with async actions
  3. react-redux: connects your redux store with ReactComponents

0 Comments