In this tutorial, I will show you how to build a Redux Toolkit CRUD example using React Hooks working with Rest API, display and modify data with Router, Axios & Bootstrap.
Security:
– React Hooks: JWT Authentication (without Redux) example
– React Hooks + Redux: JWT Authentication example
Related Posts:
– React Custom Hook
– React CRUD example with Axios and Web API (using React Components)
– React Hooks (without Redux) CRUD example with Axios and Rest API
– React Hooks File Upload example with Axios & Progress Bar
– React Table example: CRUD App | react-table 7
– React Form Validation with Hooks example
Serverless with Firebase:
– React Hooks + Firebase Realtime Database: CRUD App
– React Hooks + Firestore example: CRUD app
Redux without Redux-Toolkit:
React Hooks + Redux: CRUD example with Axios and Rest API
Using React Component:
Redux-Toolkit example with CRUD Application
Overview of Redux Toolkit CRUD example
We will build a React Redux Tutorial Application with Rest API calls in that:
- Each Tutorial has id, title, description, published status.
- We can create, retrieve, update, delete Tutorials.
- There is a Search bar for finding Tutorials by title.
Here are screenshots of our React Redux CRUD Application.
– Create a Tutorial:
– Retrieve all items:
– Click on Edit button to update an item:
On this Page, you can:
- change status to Published using Publish button
- delete the item using Delete button
- update the item details with Update button
If you need Form Validation with React Hook Form 7, please visit:
React Form Validation with Hooks example
– Search Tutorials by title:
– Check Redux State with Dev-tool:
This React Redux-Toolkit Client consumes the following Web API:
Methods | Urls | Actions |
---|---|---|
POST | /api/tutorials | create new Tutorial |
GET | /api/tutorials | retrieve all Tutorials |
GET | /api/tutorials/:id | retrieve a Tutorial by :id |
PUT | /api/tutorials/:id | update a Tutorial by :id |
DELETE | /api/tutorials/:id | delete a Tutorial by :id |
DELETE | /api/tutorials | delete all Tutorials |
GET | /api/tutorials?title=[keyword] | find all Tutorials which title contains keyword |
You can find step by step to build a Server like this in one of these posts:
– Express, Sequelize & MySQL
– Express, Sequelize & PostgreSQL
– Express, Sequelize & SQL Server
– Express & MongoDb
– Spring Boot & MySQL
– Spring Boot & PostgreSQL
– Spring Boot & MongoDB
– Spring Boot & SQL Server
– Spring Boot & H2
– Spring Boot & Cassandra
– Spring Boot & Oracle
– Django & MySQL
– Django & PostgreSQL
– Django & MongoDB
Redux-Toolkit CRUD with React Hooks Component Diagram
Now look at the React components that we’re gonna implement:
– The App
component is a container with React Router
. It has navbar
that links to routes paths.
– We have 3 pages that call async Thunks (that will take care of dispatching the right actions) which uses TutorialDataService
to interact with Rest API.
TutorialsList
gets and displays Tutorials.Tutorial
has form for editing Tutorial’s details based on:id
.AddTutorial
has form for submission new Tutorial.
– TutorialDataService
uses axios
to make HTTP requests and receive responses.
Redux-Toolkit CRUD with Hooks and Rest API example
This diagram shows how Redux elements work in our React Application:
We’re gonna create Redux store
for storing tutorials
data. Other React Components will work with the Store via calling async Thunks using React Redux Hooks API.
reducer
will take the action and return new state
. The reducer for a specific section of the Redux app state is called a “slice reducer”.
Technology
- React 18/17
- react-redux 8
- redux-toolkit 1.8.5
- react-router-dom 6
- axios 0.27.2
- bootstrap 4
Project Structure
I’m gonna explain it briefly.
– package.json contains main modules: react
, react-router-dom
, react-redux
, redux-toolkit
, axios
& bootstrap
.
– App
is the container that has Router
& navbar.
– There are 3 pages: TutorialsList
, Tutorial
, AddTutorial
.
– http-common.js initializes axios with HTTP base Url and headers.
– TutorialService
has methods for sending HTTP requests to the Apis.
– .env configures port for this Redux Toolkit CRUD App.
About Redux elements that we’re gonna use:
– store.js is where we create the Redux store instance with tutorials
reducer. Each reducer updates a different part of the application state corresponding to dispatched action.
– Reducer and actions for a single feature are defined together in each file of slices folder.
Setup React Redux Project
Open cmd at the folder you want to save Project folder, run command:
npx create-react-app redux-toolkit-example-crud-hooks
After the process is done. We create additional folders and files like the following tree:
public
src
components
AddTutorial.js
Tutorial.js
TutorialsList.js
services
TutorialService.js
slices
tutorials.js
App.css
App.js
index.js
store.js
package.json
Install Bootstrap to Redux-Toolkit CRUD App
Run command: yarn add [email protected]
.
Or npm install [email protected]
.
Open src/App.js and modify the code inside it as following-
import React from "react";
import "bootstrap/dist/css/bootstrap.min.css";
function App() {
return (
...
);
}
export default App;
Add React Router to Redux-Toolkit CRUD App
– Run the command: yarn add react-router-dom
.
Or npm install react-router-dom
.
– Open src/App.js and wrap all UI elements by BrowserRouter
object.
...
import { BrowserRouter as Router } from "react-router-dom";
function App() {
return (
<Router>
...
</Router>
);
}
export default App;
The App
component is the root container for our application, it will contain a navbar
inside <Router>
above, and also, a Routes
object with several Route
. Each Route
points to a React Component.
Now App.js looks like:
import React from "react";
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import "./App.css";
import AddTutorial from "./components/AddTutorial";
import Tutorial from "./components/Tutorial";
import TutorialsList from "./components/TutorialsList";
function App() {
return (
<Router>
<nav className="navbar navbar-expand navbar-dark bg-dark">
<a href="/tutorials" className="navbar-brand">
bezKoder
</a>
<div className="navbar-nav mr-auto">
<li className="nav-item">
<Link to={"/tutorials"} className="nav-link">
Tutorials
</Link>
</li>
<li className="nav-item">
<Link to={"/add"} className="nav-link">
Add
</Link>
</li>
</div>
</nav>
<div className="container mt-3">
<Routes>
<Route path="/" element={<TutorialsList/>} />
<Route path="/tutorials" element={<TutorialsList/>} />
<Route path="/add" element={<AddTutorial/>} />
<Route path="/tutorials/:id" element={<Tutorial/>} />
</Routes>
</div>
</Router>
);
}
export default App;
You can simplify import statement with:
Absolute Import in React
Initialize Axios
Let’s install axios with command: yarn add axios
or npm install axios
.
Under src folder, we create http-common.js file with following code:
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:8080/api",
headers: {
"Content-type": "application/json"
}
});
You can change the baseURL
that depends on REST APIs url that your Server configures.
For more details about ways to use Axios, please visit:
Axios request: Get/Post/Put/Delete example
Create Data Service
In this step, we’re gonna create a service that uses axios object above to send HTTP requests.
The service exports CRUD functions and finder method:
- CREATE:
create
- RETRIEVE:
getAll
,get
- UPDATE:
update
- DELETE:
remove
,removeAll
- FINDER:
findByTitle
services/TutorialService.js
import http from "../http-common";
const getAll = () => {
return http.get("/tutorials");
};
const get = id => {
return http.get(`/tutorials/${id}`);
};
const create = data => {
return http.post("/tutorials", data);
};
const update = (id, data) => {
return http.put(`/tutorials/${id}`, data);
};
const remove = id => {
return http.delete(`/tutorials/${id}`);
};
const removeAll = () => {
return http.delete(`/tutorials`);
};
const findByTitle = title => {
return http.get(`/tutorials?title=${title}`);
};
const TutorialService = {
getAll,
get,
create,
update,
remove,
removeAll,
findByTitle
};
export default TutorialService;
We call axios (imported as http) get
, post
, put
, delete
method corresponding to HTTP Requests: GET, POST, PUT, DELETE to make CRUD Operations.
Install react-redux and redux-toolkit
Install Redux-toolkit with the following command:
yarn add react-redux @reduxjs/toolkit
Or npm install react-redux @reduxjs/toolkit
With redux-toolkit, we don’t need to install redux-devtools-extension.
Create Slice Reducer and Actions
Instead of creating many folders and files for Redux (actions, reducers, types,…), with redux-toolkit we just need all-in-one: slice.
A slice is a collection of Redux reducer logic and actions for a single feature.
For creating a slice, we need:
- name to identify the slice
- initial state value
- one or more reducer functions to define how the state can be updated
Once a slice is created, we can export the generated Redux action creators and the reducer function for the whole slice.
Redux Toolkit provides createSlice()
function that will auto-generate the action types and action creators for you, based on the names of the reducer functions you provide.
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
// add your non-async reducers here
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
},
},
extraReducers: {
// add your async reducers here
}
})
// Action creators
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
In the code above, you can see that we write “mutating” logic in reducers. It doesn’t actually mutate the state because it uses the Immer library behind the scenes, which detects changes to a “draft state” and produces a brand new immutable state based off those changes.
We’re gonna create a Slice for tutorials
state. In src/slices folder, create file named tutorials.js.
slices
tutorials.js
We’re gonna import TutorialDataService
to make asynchronous HTTP requests with trigger one or more dispatch
in the result.
We also need to use Redux Toolkit createAsyncThunk
which provides a thunk that will take care of the action types and dispatching the right actions based on the returned promise. There are 6 async Thunks to be exported:
createTutorial
retrieveTutorials
updateTutorial
deleteTutorial
deleteAllTutorials
findTutorialsByTitle
slices/tutorials.js
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import TutorialDataService from "../services/TutorialService";
const initialState = [];
export const createTutorial = createAsyncThunk(
"tutorials/create",
async ({ title, description }) => {
const res = await TutorialDataService.create({ title, description });
return res.data;
}
);
export const retrieveTutorials = createAsyncThunk(
"tutorials/retrieve",
async () => {
const res = await TutorialDataService.getAll();
return res.data;
}
);
export const updateTutorial = createAsyncThunk(
"tutorials/update",
async ({ id, data }) => {
const res = await TutorialDataService.update(id, data);
return res.data;
}
);
export const deleteTutorial = createAsyncThunk(
"tutorials/delete",
async ({ id }) => {
await TutorialDataService.remove(id);
return { id };
}
);
export const deleteAllTutorials = createAsyncThunk(
"tutorials/deleteAll",
async () => {
const res = await TutorialDataService.removeAll();
return res.data;
}
);
export const findTutorialsByTitle = createAsyncThunk(
"tutorials/findByTitle",
async ({ title }) => {
const res = await TutorialDataService.findByTitle(title);
return res.data;
}
);
const tutorialSlice = createSlice({
name: "tutorial",
initialState,
extraReducers: {
[createTutorial.fulfilled]: (state, action) => {
state.push(action.payload);
},
[retrieveTutorials.fulfilled]: (state, action) => {
return [...action.payload];
},
[updateTutorial.fulfilled]: (state, action) => {
const index = state.findIndex(tutorial => tutorial.id === action.payload.id);
state[index] = {
...state[index],
...action.payload,
};
},
[deleteTutorial.fulfilled]: (state, action) => {
let index = state.findIndex(({ id }) => id === action.payload.id);
state.splice(index, 1);
},
[deleteAllTutorials.fulfilled]: (state, action) => {
return [];
},
[findTutorialsByTitle.fulfilled]: (state, action) => {
return [...action.payload];
},
},
});
const { reducer } = tutorialSlice;
export default reducer;
Create Redux Store
This Store will bring Actions and Reducers together and hold the Application state.
The Redux Toolkit configureStore()
function automatically:
- enable the Redux DevTools Extension.
- sets up the thunk middleware by default, so you can immediately write thunks without more configuration.
In the previous part, we exported tutorials
reducer from tutorialSlice
. Let’s import it, and pass it to configureStore()
:
store.js
import { configureStore } from '@reduxjs/toolkit'
import tutorialReducer from './slices/tutorials';
const reducer = {
tutorials: tutorialReducer
}
const store = configureStore({
reducer: reducer,
devTools: true,
})
export default store;
If you have Auth Reducer that manages authentication logic, you can add it to reducer
object like following code:
import { configureStore } from '@reduxjs/toolkit'
import tutorialReducer from './slices/tutorials';
import authReducer from "./slices/auth";
const reducer = {
tutorials: tutorialReducer,
auth: authReducer
}
const store = configureStore({
reducer: reducer,
devTools: true,
})
export default store;
Provide State to React Pages
We do it by wrapping our entire application in a <Provider>
component to make the store
available to its child components.
Provider
is an high order component that wraps up React application and makes it aware of the entire Redux store
. That is, it provides the store
to its child components.
Now we want our entire React App to access the store
, just put the App
Component within Provider
.
Open src/index.js
import React from 'react';
import { createRoot } from "react-dom/client";
import App from './App';
...
import { Provider } from 'react-redux';
import store from './store';
const container = document.getElementById("root");
const root = createRoot(container);
root.render(
<Provider store={store}>
<App />
</Provider>
);
Create React Pages
Now we’re gonna build 3 components corresponding to 3 Routes defined before.
Add Object
This component has a Form to submit new Tutorial with 2 fields: title
& description
.
components/AddTutorial.js
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { createTutorial } from "../slices/tutorials";
const AddTutorial = () => {
const initialTutorialState = {
id: null,
title: "",
description: "",
published: false
};
const [tutorial, setTutorial] = useState(initialTutorialState);
const [submitted, setSubmitted] = useState(false);
const dispatch = useDispatch();
const handleInputChange = event => {
const { name, value } = event.target;
setTutorial({ ...tutorial, [name]: value });
};
const saveTutorial = () => {
const { title, description } = tutorial;
dispatch(createTutorial({ title, description }))
.unwrap()
.then(data => {
console.log(data);
setTutorial({
id: data.id,
title: data.title,
description: data.description,
published: data.published
});
setSubmitted(true);
})
.catch(e => {
console.log(e);
});
};
const newTutorial = () => {
setTutorial(initialTutorialState);
setSubmitted(false);
};
return (
...
);
};
export default AddTutorial;
First, we define and set initial state: tutorial
& submitted
.
Next, we create handleInputChange()
function to track the values of the input and set that state for changes.
We also have a function to get tutorial
local state and send the POST request to the Web API. It dispatchs async Thunk createTutorial()
with useDispatch()
. This hook returns a reference to the dispatch
function from the Redux store.
const dispatch = useDispatch();
dispatch(createTutorial(...));
How about handling the Thunk result?
dispatch(createTutorial(...)).then(() => {
// do something
})
Thunks generated by createAsyncThunk
will always return a resolved promise with either the fulfilled
action object or rejected
action object inside.
The promise has an unwrap
property which can be called to extract the payload of a fulfilled
action or to throw the error
.
dispatch(createTutorial(...))
.unwrap()
.then((res) => {
// do something
})
.catch((err) => {
// handle error
})
For return
, we check the submitted
state, if it is true, we show Add button for creating new Tutorial again. Otherwise, a Form with Submit button will display.
...
const AddTutorial = () => {
...
return (
<div className="submit-form">
{submitted ? (
<div>
<h4>You submitted successfully!</h4>
<button className="btn btn-success" onClick={newTutorial}>
Add
</button>
</div>
) : (
<div>
<div className="form-group">
<label htmlFor="title">Title</label>
<input
type="text"
className="form-control"
id="title"
required
value={tutorial.title || ''}
onChange={handleInputChange}
name="title"
/>
</div>
<div className="form-group">
<label htmlFor="description">Description</label>
<input
type="text"
className="form-control"
id="description"
required
value={tutorial.description || ''}
onChange={handleInputChange}
name="description"
/>
</div>
<button onClick={saveTutorial} className="btn btn-success">
Submit
</button>
</div>
)}
</div>
);
};
export default AddTutorial;
List of Objects
This component has:
- a search bar for finding Tutorials by title.
- a tutorials array displayed as a list on the left.
- a selected Tutorial which is shown on the right.
Beside global state tutorials
, we also have following local state:
searchTitle
currentTutorial
andcurrentIndex
We also need to use 3 async Thunks:
retrieveTutorials
findTutorialsByTitle
deleteAllTutorials
To connect the Redux store with local Component state and props, we use useSelector()
and useDispatch()
.
const tutorials = useSelector(state => state.tutorials);
const dispatch = useDispatch();
When using useSelector
with an inline selector above, a new instance of the selector is created whenever the component is rendered.
Now we can work with tutorials
state and dispatch actions like this:
// use state
tutorials.map(...);
// dispatch actions
dispatch(retrieveTutorials());
dispatch(findTutorialsByTitle(...));
dispatch(deleteAllTutorials(...));
components/TutorialsList.js
import React, { useState, useEffect, useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
retrieveTutorials,
findTutorialsByTitle,
deleteAllTutorials,
} from "../slices/tutorials";
import { Link } from "react-router-dom";
const TutorialsList = () => {
const [currentTutorial, setCurrentTutorial] = useState(null);
const [currentIndex, setCurrentIndex] = useState(-1);
const [searchTitle, setSearchTitle] = useState("");
const tutorials = useSelector(state => state.tutorials);
const dispatch = useDispatch();
const onChangeSearchTitle = e => {
const searchTitle = e.target.value;
setSearchTitle(searchTitle);
};
const initFetch = useCallback(() => {
dispatch(retrieveTutorials());
}, [dispatch])
useEffect(() => {
initFetch()
}, [initFetch])
const refreshData = () => {
setCurrentTutorial(null);
setCurrentIndex(-1);
};
const setActiveTutorial = (tutorial, index) => {
setCurrentTutorial(tutorial);
setCurrentIndex(index);
};
const removeAllTutorials = () => {
dispatch(deleteAllTutorials())
.then(response => {
refreshData();
})
.catch(e => {
console.log(e);
});
};
const findByTitle = () => {
refreshData();
dispatch(findTutorialsByTitle({ title: searchTitle }));
};
return (
...
);
};
export default TutorialsList;
Let’s continue to implement UI elements:
...
import { Link } from "react-router-dom";
const TutorialsList = () => {
...
return (
<div className="list row">
<div className="col-md-8">
<div className="input-group mb-3">
<input
type="text"
className="form-control"
placeholder="Search by title"
value={searchTitle}
onChange={onChangeSearchTitle}
/>
<div className="input-group-append">
<button
className="btn btn-outline-secondary"
type="button"
onClick={findByTitle}
>
Search
</button>
</div>
</div>
</div>
<div className="col-md-6">
<h4>Tutorials List</h4>
<ul className="list-group">
{tutorials &&
tutorials.map((tutorial, index) => (
<li
className={
"list-group-item " + (index === currentIndex ? "active" : "")
}
onClick={() => setActiveTutorial(tutorial, index)}
key={index}
>
{tutorial.title}
</li>
))}
</ul>
<button
className="m-3 btn btn-sm btn-danger"
onClick={removeAllTutorials}
>
Remove All
</button>
</div>
<div className="col-md-6">
{currentTutorial ? (
<div>
<h4>Tutorial</h4>
<div>
<label>
<strong>Title:</strong>
</label>{" "}
{currentTutorial.title}
</div>
<div>
<label>
<strong>Description:</strong>
</label>{" "}
{currentTutorial.description}
</div>
<div>
<label>
<strong>Status:</strong>
</label>{" "}
{currentTutorial.published ? "Published" : "Pending"}
</div>
<Link
to={"/tutorials/" + currentTutorial.id}
className="badge badge-warning"
>
Edit
</Link>
</div>
) : (
<div>
<br />
<p>Please click on a Tutorial...</p>
</div>
)}
</div>
</div>
);
};
export default TutorialsList;
If you click on Edit button of any Tutorial, the app will direct you to Tutorial page.
We use React Router Link
for accessing that page with url: /tutorials/:id
.
Object details
This component will use TutorialDataService.get()
method in the Effect Hook useEffect()
to get Tutorial by id in the URL.
For update, delete the Tutorial, we work with following async Thunks:
updateTutorial
deleteTutorial
components/Tutorial.js
import React, { useState, useEffect } from "react";
import { useDispatch } from "react-redux";
import { useParams, useNavigate } from 'react-router-dom';
import { updateTutorial, deleteTutorial } from "../slices/tutorials";
import TutorialDataService from "../services/TutorialService";
const Tutorial = (props) => {
const { id }= useParams();
let navigate = useNavigate();
const initialTutorialState = {
id: null,
title: "",
description: "",
published: false
};
const [currentTutorial, setCurrentTutorial] = useState(initialTutorialState);
const [message, setMessage] = useState("");
const dispatch = useDispatch();
const getTutorial = id => {
TutorialDataService.get(id)
.then(response => {
setCurrentTutorial(response.data);
})
.catch(e => {
console.log(e);
});
};
useEffect(() => {
if (id)
getTutorial(id);
}, [id]);
const handleInputChange = event => {
const { name, value } = event.target;
setCurrentTutorial({ ...currentTutorial, [name]: value });
};
const updateStatus = status => {
const data = {
id: currentTutorial.id,
title: currentTutorial.title,
description: currentTutorial.description,
published: status
};
dispatch(updateTutorial({ id: currentTutorial.id, data }))
.unwrap()
.then(response => {
console.log(response);
setCurrentTutorial({ ...currentTutorial, published: status });
setMessage("The status was updated successfully!");
})
.catch(e => {
console.log(e);
});
};
const updateContent = () => {
dispatch(updateTutorial({ id: currentTutorial.id, data: currentTutorial }))
.unwrap()
.then(response => {
console.log(response);
setMessage("The tutorial was updated successfully!");
})
.catch(e => {
console.log(e);
});
};
const removeTutorial = () => {
dispatch(deleteTutorial({ id: currentTutorial.id }))
.unwrap()
.then(() => {
navigate("/tutorials");
})
.catch(e => {
console.log(e);
});
};
return (
...
);
};
export default Tutorial;
And this is the code inside return
:
...
const Tutorial = (props) => {
...
return (
<div>
{currentTutorial ? (
<div className="edit-form">
<h4>Tutorial</h4>
<form>
<div className="form-group">
<label htmlFor="title">Title</label>
<input
type="text"
className="form-control"
id="title"
name="title"
value={currentTutorial.title}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="description">Description</label>
<input
type="text"
className="form-control"
id="description"
name="description"
value={currentTutorial.description}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label>
<strong>Status:</strong>
</label>
{currentTutorial.published ? "Published" : "Pending"}
</div>
</form>
{currentTutorial.published ? (
<button
className="badge badge-primary mr-2"
onClick={() => updateStatus(false)}
>
UnPublish
</button>
) : (
<button
className="badge badge-primary mr-2"
onClick={() => updateStatus(true)}
>
Publish
</button>
)}
<button className="badge badge-danger mr-2" onClick={removeTutorial}>
Delete
</button>
<button
type="submit"
className="badge badge-success"
onClick={updateContent}
>
Update
</button>
<p>{message}</p>
</div>
) : (
<div>
<br />
<p>Please click on a Tutorial...</p>
</div>
)}
</div>
);
};
export default Tutorial;
Add CSS style for React Pages
Open src/App.css and write CSS code as following:
.list {
text-align: left;
max-width: 750px;
margin: auto;
}
.submit-form {
max-width: 300px;
margin: auto;
}
.edit-form {
max-width: 300px;
margin: auto;
}
Configure Port for Redux-Toolkit CRUD with Hooks & Web API
Because most of HTTP Server use CORS configuration that accepts resource sharing retrictted to some sites or ports, so we also need to configure port for our App.
In project folder, create .env file with following content:
PORT=8081
Now we’ve set our app running at port 8081
.
Run React Hooks Redux CRUD App
You can run our App with command: yarn start
or npm run start
.
If the process is successful, open Browser with Url: http://localhost:8081/
and check it.
This React Client will work well with following back-end Rest APIs:
– Express, Sequelize & MySQL
– Express, Sequelize & PostgreSQL
– Express, Sequelize & SQL Server
– Express & MongoDb
– Spring Boot & MySQL
– Spring Boot & PostgreSQL
– Spring Boot & MongoDB
– Spring Boot & SQL Server
– Spring Boot & H2
– Spring Boot & Cassandra
– Spring Boot & Oracle
– Django & MySQL
– Django & PostgreSQL
– Django & MongoDB
Conclusion
Today we’ve built a React Hooks Redux CRUD example successfully with React Router & Axios. Now we can consume REST APIs, display, search and modify data with Redux Store in a clean way. I hope you can make API call (GET/POST/PUT/DELETE) in your project at ease.
If you don’t want to use Redux:
React Hooks (without Redux) CRUD example with Axios and Web API
Implement Security:
– React Hooks: JWT Authentication (without Redux) example
– React Hooks + Redux: JWT Authentication example
You can add Pagination Component:
React Pagination using Hooks example
Or Form Validation:
React Form Validation with Hooks example
Happy learning, see you again!
Further Reading
- React Router Guide
- React Hooks
- React Custom Hook
- Redux Tutorials
- Redux Toolkit Usage Guide
- In-depth Introduction to JWT-JSON Web Token
For more details about ways to use Axios, please visit:
Axios request: Get/Post/Put/Delete example
Fullstack:
– Spring Boot + React Redux example: Build a CRUD App
– React + Spring Boot + MySQL: CRUD example
– React + Spring Boot + PostgreSQL: CRUD example
– React + Spring Boot + MongoDB: CRUD example
– React + Node.js + Express + MySQL: CRUD example
– React Redux + Node.js + Express + MySQL: CRUD example
– React + Node.js + Express + PostgreSQL example
– React + Node.js + Express + MongoDB example
– React + Django + Rest Framework example
Source Code
You can find the complete source code for this example on Github.
Redux without Redux-Toolkit:
React Hooks + Redux: CRUD example with Axios and Rest API
Using React Component:
Redux-Toolkit example with CRUD Application
Parabéns, seus tutoriais são excelentes..
Congratulations, your tutorials are excellent..