Thunk le middleware Redux Thunk est utilisé pour permettre aux actions de Redux d'être des fonctions plutôt que des objets.
Ces fonctions sont appelées "thunks". Le middleware Redux Thunk intercepte ces fonctions avant qu'elles n'atteignent les Reducers,
leur permettant d'effectuer des opérations asynchrones avant de déclencher les actions appropriées.
Action simple
const fetchUser = (userId) => ({
type: 'FETCH_USER',
payload: userId,
});
une fois on appelle cette action on envoie (userId) au reducer qui exécute l'action puis retourne les résultats
Supposons maintenant que les données du user sont récupéré à partir d'une api
const fetchUser = (userId) => ({
function (){
axios.get("http://localhost:3004/users/"+userId).then((res)=>{
type: 'FETCH_USER',
payload: res.data,
})}
});
Erreur car l'action doit retouner un objet et non une fonction
objet:
{
type:"",
payload:"",
data1:"",
...
}
donc afin de retourné une fonction dans l'action on doit créer un
Thunk action au lieu de action simple:
// Thunk action creator
const fetchUser = (userId) => {
return (dispatch, getState) => {
axios.get(`http://localhost:3004/users/${userId}`)
.then((res) => {
// appeler l'action du reducer
dispatch({
type: 'FETCH_USER',
payload: res.data,
});
})
.catch((error) => {
// si Erreur appler l'action FETCH_USER_FAILURE du reducer
dispatch({
type: 'FETCH_USER_FAILURE',
payload: error.message,
});
});
};
};
Remarque
Dans un projet CRUD pour une API on doit utiliser Redux et thunk Action
Exemples:Clients Crud
Afin d'exécuter les Requests http on a besoin d'un serveur backEnd on peut créer un serveur de test à l'aide de json-server
npm install -g json-server
npm install json-server
json-server --watch db.json --port 3004
Le fichier db.json est Crée changer le contenu de ce fichier à:
{"clients": [{
"id": 1,
"name": "nom1",
"email": "email1@gm"
}]}
puis relancer le serveur
json-server --watch db.json --port 3004
Remarque: Si vous avez une erreur veuillez lancer la commande suivante:
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process
json-server --watch db.json --port 3004
Installer Axios
le module
Axios de React pour consommer les Rest Apis crées dans nodeJs project
npm install axios --save
installer
redux-thunk
npm install redux-thunk
Créer le fichier redux_api_thunk/redux/clientActions.js
// clientActions.js
export const getClients = (clients) => ({
type: "GET_CLIENTS",
payload: clients });
export const errorGetClient = (error) => ({
type: "Error_GET_CLIENTS",
payload: error });
Créer le fichier redux_api_thunk/redux/clientThunk.js
//clientThunk.js
import axios from 'axios';
import {
getClients,
errorGetClient,
} from './clientActions';
export const getClients_thunk = () => {
return async (dispatch) => {
try {
const response = await axios.get('http://localhost:3004/clients');
dispatch(getClients(response.data));
} catch (error) {
dispatch(errorGetClient(error.message));
}
};
};
Créer le fichier redux_api_thunk/redux/clientReducer.js
const initialState = {
clients: [],
loading: false,
error: null,
};
const clientReducer = (state = initialState, action) => {
switch (action.type) {
case "GET_CLIENTS":
return { ...state,
clients: action.payload,
loading: false,
error: null };
case "Error_GET_CLIENTS":
return { ...state,
loading: false,
error: action.payload };
default:
return state;
}
};
export default clientReducer;
Créer le fichier redux_api_thunk/redux/store.js
import { createStore, applyMiddleware } from 'redux';
import {thunk} from 'redux-thunk';
import clientReducer from './clientReducer';
const store = createStore(clientReducer, applyMiddleware(thunk));
export default store;
App.js
// App.js
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getClients_thunk } from './redux_api_thunk/redux/clientsThunk';
const App = () => {
const dispatch = useDispatch();
const clients = useSelector((state) => state.clients);
const loading = useSelector((state) => state.loading);
const error = useSelector((state) => state.error);
useEffect(() => {
dispatch(getClients_thunk());
}, [dispatch]);
return (
<div>
<h1>Client Manager</h1>
{loading && <p>Loading...</p>}
{error && <p>Error: {error}</p>}
<ul>
{clients.map((client) => (
<li key={client.id}>
{client.name} ({client.email}){' '}
<button onClick={() => handleDeleteClient(client.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
};
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import reportWebVitals from './reportWebVitals';
import store from './redux_api_thunk/redux/store'
import { Provider } from 'react-redux';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>
);
reportWebVitals();
Tester
npm start
Ajouter les autres actions
// Clientctions.js
export const FETCH_CLIENTS_REQUEST = 'FETCH_CLIENTS_REQUEST';
export const FETCH_CLIENTS_SUCCESS = 'FETCH_CLIENTS_SUCCESS';
export const FETCH_CLIENTS_FAILURE = 'FETCH_CLIENTS_FAILURE';
export const ADD_CLIENT_REQUEST = 'ADD_CLIENT_REQUEST';
export const ADD_CLIENT_SUCCESS = 'ADD_CLIENT_SUCCESS';
export const ADD_CLIENT_FAILURE = 'ADD_CLIENT_FAILURE';
export const DELETE_CLIENT_REQUEST = 'DELETE_CLIENT_REQUEST';
export const DELETE_CLIENT_SUCCESS = 'DELETE_CLIENT_SUCCESS';
export const DELETE_CLIENT_FAILURE = 'DELETE_CLIENT_FAILURE';
export const fetchClientsRequest = () => ({ type: FETCH_CLIENTS_REQUEST });
export const fetchClientsSuccess = (clients) => ({ type: FETCH_CLIENTS_SUCCESS, payload: clients });
export const fetchClientsFailure = (error) => ({ type: FETCH_CLIENTS_FAILURE, payload: error });
export const addClientRequest = () => ({ type: ADD_CLIENT_REQUEST });
export const addClientSuccess = (client) => ({ type: ADD_CLIENT_SUCCESS, payload: client });
export const addClientFailure = (error) => ({ type: ADD_CLIENT_FAILURE, payload: error });
export const deleteClientRequest = () => ({ type: DELETE_CLIENT_REQUEST });
export const deleteClientSuccess = (clientId) => ({ type: DELETE_CLIENT_SUCCESS, payload: clientId });
export const deleteClientFailure = (error) => ({ type: DELETE_CLIENT_FAILURE, payload: error });
// Clienthunks.js
import axios from 'axios';
import {
fetchClientsRequest,
fetchClientsSuccess,
fetchClientsFailure,
addClientRequest,
addClientSuccess,
addClientFailure,
deleteClientRequest,
deleteClientSuccess,
deleteClientFailure,
} from './clientActions';
const API_BASE_URL = 'http://localhost:3004/clients';
export const fetchClients = () => {
return async (dispatch) => {
dispatch(fetchClientsRequest());
try {
const response = await axios.get(API_BASE_URL);
dispatch(fetchClientsSuccess(response.data));
} catch (error) {
dispatch(fetchClientsFailure(error.message));
}
};
};
export const addClient = (client) => {
return async (dispatch) => {
dispatch(addClientRequest());
try {
const response = await axios.post(API_BASE_URL, client);
dispatch(addClientSuccess(response.data));
} catch (error) {
dispatch(addClientFailure(error.message));
}
};
};
export const deleteClient = (clientId) => {
return async (dispatch) => {
dispatch(deleteClientRequest());
try {
await axios.delete(`${API_BASE_URL}/${clientId}`);
dispatch(deleteClientSuccess(clientId));
} catch (error) {
dispatch(deleteClientFailure(error.message));
}
};
};
// clientReducer.js
import {
FETCH_CLIENTS_REQUEST,
FETCH_CLIENTS_SUCCESS,
FETCH_CLIENTS_FAILURE,
ADD_CLIENT_REQUEST,
ADD_CLIENT_SUCCESS,
ADD_CLIENT_FAILURE,
DELETE_CLIENT_REQUEST,
DELETE_CLIENT_SUCCESS,
DELETE_CLIENT_FAILURE,
} from './clientActions';
const initialState = {
clients: [],
loading: false,
error: null,
};
const clientReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_CLIENTS_REQUEST:
case ADD_CLIENT_REQUEST:
case DELETE_CLIENT_REQUEST:
return { ...state, loading: true, error: null };
case FETCH_CLIENTS_SUCCESS:
return { ...state, clients: action.payload, loading: false, error: null };
case ADD_CLIENT_SUCCESS:
return { ...state, clients: [...state.clients, action.payload], loading: false, error: null };
case DELETE_CLIENT_SUCCESS:
return {
...state,
clients: state.clients.filter((client) => client.id !== action.payload),
loading: false,
error: null,
};
case FETCH_CLIENTS_FAILURE:
case ADD_CLIENT_FAILURE:
case DELETE_CLIENT_FAILURE:
return { ...state, loading: false, error: action.payload };
default:
return state;
}
};
export default clientReducer;