Friday 24 November 2023

CRUD operation using React Redux

 We will see below CRUD operation step by step

On Click on Delete button

On Click on Add button
On Click on Edit button
Need to install some supporting dependency

Folder Structure


Below steps to follow
>npx create-react-app react-redux-crud-operation
>cd react-redux-crud-operation
>code .
>open terminal
>npm i bootstrap
---Then import bootstrap in index.js file
-----import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
>npm i react-router-dom
---Then initiate routing in App.js file
>npm i react-toastify
---Then import bootstrap in index.js file
-----import '../node_modules/react-toastify/dist/ReactToastify.css';
---Then include toast container in App.js file
-----<ToastContainer></ToastContainer>
>npm i redux react-redux redux-thunk axios redux-logger @reduxjs/toolkit
>npm i -g json-server
>json-server --watch src/Data/db.json --port 8000
---Default db.json file will be created with default data --You can modify with your needed..
>npm start

--Start from ActionType>Action>Reducer>Store

--To create json-server
>npm install -g json-server
>json-server --watch src/Data/db.json -p 8000

I. In Data/db.json file and below are code snippets
{
  "user": [
    {
      "id": 1,
      "name": "Subs",
      "role": "Admin",
      "email": "s@p.com",
      "phone": "739187391"
    },
]
}

II. In Redux/Action.jsx file and below are code snippets
import axios from "axios";
import { ADD_USER, DELETE_USER, FAIL_REQUEST, GET_USER_LIST, GET_USER_OBJ, MAKE_REQUEST, UPDATE_USER } from "./ActionType";
import { toast } from "react-toastify";

export const makeRequest = () => {
  return {
    type: MAKE_REQUEST,
  };
};

export const failRequest = (err) => {
  return {
    type: FAIL_REQUEST,
    payload: err,
  };
};

export const getUserList = (data) => {
  return {
    type: GET_USER_LIST,
    payload: data,
  };
};

export const deleteUser = () => {
  return {
    type: DELETE_USER,
  };
};

export const addUser = () => {
  return {
    type: ADD_USER,
  };
};

export const updateUser = () => {
  return {
    type: UPDATE_USER,
  };
};

export const getUserObj = (data) => {
  return {
    type: GET_USER_OBJ,
    payload:data
  };
};

export const FetchUserList = () => {
  return (dispatch) => {
    dispatch(makeRequest());
    //setTimeout(()=>{

    axios
      .get("http://localhost:8000/user")
      .then((res) => {
        const userlist = res.data;
        dispatch(getUserList(userlist));
      })
      .catch((err) => {
        dispatch(failRequest(err.message));
      })

    //},2000)
  };
};

export const DeleteUser = (id) => {
  return (dispatch) => {
    dispatch(makeRequest());
    //setTimeout(()=>{

    axios
      .delete("http://localhost:8000/user/"+id)
      .then((res) => {
        dispatch(deleteUser());
      })
      .catch((err) => {
        dispatch(failRequest(err.message));
      })

    //},2000)
  };
};

export const AddUser = (data) => {
  return (dispatch) => {
    dispatch(makeRequest());
    //setTimeout(()=>{

    axios
      .post("http://localhost:8000/user/",data)
      .then((res) => {
        dispatch(addUser());
        toast.success("User added successfully");
      })
      .catch((err) => {
        dispatch(failRequest(err.message));
      })

    //},2000)
  };
};

export const UpdateUser = (data,id) => {
  return (dispatch) => {
    dispatch(makeRequest());
    //setTimeout(()=>{

    axios
      .put("http://localhost:8000/user/"+id,data)
      .then((res) => {
        dispatch(updateUser());
        toast.success("User update successfully");
      })
      .catch((err) => {
        dispatch(failRequest(err.message));
      })

    //},2000)
  };
};

export const FetchUserObj = (id) => {
  return (dispatch) => {
    dispatch(makeRequest());
    //setTimeout(()=>{

    axios
      .get("http://localhost:8000/user/"+id)
      .then((res) => {
        const userlist = res.data;
        dispatch(getUserObj(userlist));
      })
      .catch((err) => {
        dispatch(failRequest(err.message));
      })

    //},2000)
  };
};

III. In Redux/ActionType.jsx file and below are code snippets
export const MAKE_REQUEST='MAKE_REQUEST'
export const FAIL_REQUEST='FAIL_REQUEST'
export const GET_USER_LIST='GET_USER_LIST'
export const DELETE_USER='DELETE_USER'
export const ADD_USER='ADD_USER'
export const UPDATE_USER='UPDATE_USER'
export const GET_USER_OBJ='GET_USER_OBJ'

IV. In Redux/Reducer.jsx file and below are code snippets
import { ADD_USER, DELETE_USER, FAIL_REQUEST, GET_USER_LIST, GET_USER_OBJ, MAKE_REQUEST, UPDATE_USER } from "./ActionType"

const initialstate={
    loading:true,
    userlist:[],
    userobj:{},
    errmessage:''
}

export const Reducer=(state=initialstate,action)=>{
    switch(action.type){
        case MAKE_REQUEST:
            return{
                ...state,
                loading:true
            }
        case FAIL_REQUEST:
            return{
                ...state,
                loading:false,
                errmessage:action.payload
            }
        case GET_USER_LIST:
                return{
                    loading:false,
                    errmessage:'',
                    userlist:action.payload,
                    userobj:{}
                }
        case DELETE_USER:
                    return{
                        ...state,
                        loading:false
                    }
        case ADD_USER:
                        return{
                            ...state,
                            loading:false
                        }
        case UPDATE_USER:
                            return{
                                ...state,
                                loading:false
                            }
        case GET_USER_OBJ:
                            return{
                                ...state,
                                loading:false,
                                userobj:action.payload
                            }
        default:return state
    }
}

V. In Redux/Store.jsx file and below are code snippets
import { combineReducers, configureStore } from "@reduxjs/toolkit";
import { Reducer } from "./Reducer";
import thunk from "redux-thunk";
import logger from "redux-logger";

const rootreducer=combineReducers({user:Reducer});
const Store=configureStore({reducer:rootreducer,middleware:[thunk,logger]});
export default Store;

VI. In Component/Userlisting.jsx file and below are code snippets
import React, { useEffect } from 'react'
import { DeleteUser, FetchUserList } from '../Redux/Action'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom';
import {toast} from 'react-toastify';

function Userlisting(props) {

  useEffect(()=>{
    props.loaduser();
  },[])

  const handleDelete=(id)=>{
    if(window.confirm('Do you want to Delete user?')){
      props.deleteuser(id);
      props.loaduser();
      toast.success("User deleted successfully");
    }
  }

  return (
    props.user.loading?<div><h2>Loading..</h2></div>:
    props.user.errmessage?<div><h2>{props.user.errmessage}</h2></div>:

    <div>
      <div className='card'>
        <div className='card-header'>
        <Link to={'/user/add'} className='btn btn-success'>Add User [+]</Link>
        </div>
        <div className='card-body'>
          <table className='table table-bordered'>
            <thead>
              <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Email</th>
                <th>Phone</th>
                <th>Role</th>
                <th>Action</th>
              </tr>
            </thead>
            <tbody>
              {
                props.user.userlist && props.user.userlist.map(item=>
                  <tr key={item.id}>
                    <td>{item.id}</td>
                    <td>{item.name}</td>
                    <td>{item.email}</td>
                    <td>{item.phone}</td>
                    <td>{item.role}</td>
                    <td>
                      <Link to={`/user/edit/${item.id}`} className='btn btn-primary'>Edit</Link> |
                      <button onClick={()=>handleDelete(item.id)} className='btn btn-danger'>Delete</button>
                    </td>
                  </tr>
                  )
              }

            </tbody>

          </table>
        </div>
      </div>
    </div>
  )
}

const mapStateToProps=(state)=>{
  return{
    user:state.user
  }
}

const mapDispatchToProps=(dispatch)=>{
  return{
    loaduser:()=>dispatch(FetchUserList()),
    deleteuser:(id)=>dispatch(DeleteUser(id))
  }
}

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

VII. In Component/Adduser.jsx file and below are code snippets
import React, { useState } from 'react'
import { useDispatch } from 'react-redux';
import { Link, useNavigate } from 'react-router-dom'
import { AddUser } from '../Redux/Action';

function Adduser() {
  const [name,setname]=useState('');
  const [email,setemail]=useState('');
  const [phone,setphone]=useState('');
  const [role,setrole]=useState('staff');
  const dispatch=useDispatch();
  const navigate=useNavigate();

  const handleSubmit=(e)=>{
    e.preventDefault();
    const userobj={name,email,phone,role};
    console.log(userobj);
    dispatch(AddUser(userobj));
    navigate('/user');
  }

  return (
    <div>
      <form onSubmit={handleSubmit}>
      <div className='card'>
        <div className='card-header' style={{textAlign:'left'}}>
          <h2>Add User</h2>
        </div>
        <div className='card-body' style={{textAlign:'left'}}>
          <div className='row'>
            <div className='col-lg-12'>
              <div className='form-group'>
                <label>Name</label>
                <input value={name} onChange={e=>setname(e.target.value)} className='form-control'></input>
              </div>
            </div>
            <div className='col-lg-12'>
              <div className='form-group'>
                <label>Email</label>
                <input value={email} onChange={e=>setemail(e.target.value)} className='form-control'></input>
              </div>
            </div>
            <div className='col-lg-12'>
              <div className='form-group'>
                <label>Phone</label>
                <input value={phone} onChange={e=>setphone(e.target.value)} type='number' className='form-control'></input>
              </div>
            </div>
            <div className='col-lg-12'>
              <div className='form-group'>
                <label>Role</label>
                <input value={role} onChange={e=>setrole(e.target.value)} className='form-control'></input>
              </div>
            </div>
          </div>

        </div>
        <div className='card-footer' style={{textAlign:'left'}}>
          <button className='btn btn-primary' type='submit'>Submit</button> |
          <Link to={'/user'} className='btn btn-danger'>Back</Link>
        </div>
      </div>
      </form>
    </div>
  )
}

export default Adduser

VIII. In Component/Updateuser.jsx file and below are code snippets
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { FetchUserObj, UpdateUser } from '../Redux/Action';

function Updateuser() {
  const [code,setcode]=useState('');
  const [name,setname]=useState('');
  const [email,setemail]=useState('');
  const [phone,setphone]=useState('');
  const [role,setrole]=useState('staff');
  const dispatch=useDispatch();
  const navigate=useNavigate();
  const {id}= useParams();
  const userobj=useSelector((state)=>state.user.userobj);

  const handleSubmit=(e)=>{
    e.preventDefault();
    const userobj={id,name,email,phone,role};
    console.log(userobj);
    dispatch(UpdateUser(userobj,id));
    navigate('/user');
  }

  useEffect(()=>{
    dispatch(FetchUserObj(id));
  },[]);

  useEffect(()=>{
    if(userobj){
      setcode(userobj.id);
      setname(userobj.name);
      setemail(userobj.email);
      setphone(userobj.phone);
      setrole(userobj.role);
    }
  },[userobj]);

  return (
    <div>
      <form onSubmit={handleSubmit}>
      <div className='card'>
        <div className='card-header' style={{textAlign:'left'}}>
          <h2>Add User</h2>
        </div>
        <div className='card-body' style={{textAlign:'left'}}>
          <div className='row'>
          <div className='col-lg-12'>
              <div className='form-group'>
                <label>ID</label>
                <input value={code || ''} disabled className='form-control'></input>
              </div>
            </div>
            <div className='col-lg-12'>
              <div className='form-group'>
                <label>Name</label>
                <input value={name || ''} onChange={e=>setname(e.target.value)} className='form-control'></input>
              </div>
            </div>
            <div className='col-lg-12'>
              <div className='form-group'>
                <label>Email</label>
                <input value={email || ''} onChange={e=>setemail(e.target.value)} className='form-control'></input>
              </div>
            </div>
            <div className='col-lg-12'>
              <div className='form-group'>
                <label>Phone</label>
                <input value={phone || ''} onChange={e=>setphone(e.target.value)} type='number' className='form-control'></input>
              </div>
            </div>
            <div className='col-lg-12'>
              <div className='form-group'>
                <label>Role</label>
                <input value={role || ''} onChange={e=>setrole(e.target.value)} className='form-control'></input>
              </div>
            </div>
          </div>

        </div>
        <div className='card-footer' style={{textAlign:'left'}}>
          <button className='btn btn-primary' type='submit'>Submit</button> |
          <Link to={'/user'} className='btn btn-danger'>Back</Link>
        </div>
      </div>
      </form>
    </div>
  )
}

export default Updateuser

IX. In Component/Home.jsx file and below are code snippets
import React from 'react'

function Home() {
  return (
    <div>Home</div>
  )
}

export default Home

X. In index.js file and below are code snippets
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import '../node_modules/react-toastify/dist/ReactToastify.css';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

XI. In App.css file and below are code snippets
.header{
  width: 100%;
  background-color: blue;
  color: #fff;
  padding: 1%;
  text-align: left;
}
a{
  text-decoration: none!important;
  color: white!important;
  padding: 2px;
}
.card-header{
  text-align: right;
}

XII. In App.js file and below are code snippets
import logo from './logo.svg';
import './App.css';
import { BrowserRouter, Link, Route, Routes } from 'react-router-dom';
import Home from './Component/Home';
import Userlisting from './Component/Userlisting';
import Adduser from './Component/Adduser';
import Updateuser from './Component/Updateuser';
import { ToastContainer } from 'react-toastify';
import { Provider } from 'react-redux';
import Store from './Redux/Store';

function App() {
  return (
    <Provider store={Store}>
    <div className="App">
      <BrowserRouter>
      <div className='header'>
        <Link to={'/'}>Home</Link>
        <Link to={'/user'}>User</Link>
      </div>
      <Routes>
        <Route path='/' element={<Home></Home>}></Route>
        <Route path='/user' element={<Userlisting></Userlisting>}></Route>
        <Route path='/user/add' element={<Adduser></Adduser>}></Route>
        <Route path='/user/edit/:id' element={<Updateuser></Updateuser>}></Route>
      </Routes>
      </BrowserRouter>
      <ToastContainer position='bottom-right'></ToastContainer>
    </div>
    </Provider>
  );
}

export default App;

No comments:

Post a Comment