0% found this document useful (0 votes)
6 views98 pages

React API Integration and useEffect Guide

This document provides an overview of API connections in React, detailing the use of the ReqRes.in API for testing and the implementation of the useEffect and useMemo hooks for managing side effects and optimizing performance. It also covers authentication processes, local and session storage management, and the creation of protected routes in a React application. Additionally, it includes workshops and corrections for practical coding exercises related to these concepts.

Uploaded by

best gamer
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views98 pages

React API Integration and useEffect Guide

This document provides an overview of API connections in React, detailing the use of the ReqRes.in API for testing and the implementation of the useEffect and useMemo hooks for managing side effects and optimizing performance. It also covers authentication processes, local and session storage management, and the creation of protected routes in a React application. Additionally, it includes workshops and corrections for practical coding exercises related to these concepts.

Uploaded by

best gamer
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

@ReactJs Chapter 3

API CONNECTION
Presented by Ahmed Fakhfakh and Mohamed Bouattour
Part 1: Introduction to APIs
What is an API?

API Application Programming Interface

It's a bridge between your frontend (React) and your backend (server).

Usage Example
GET Retrieve data Read the list of users
POST Create data Register, log in
PUT Edit data Update a profile
DELETE Delete data Delete an account
Advantage: No Backend to Create

We will use [Link] - a free REST API - for testing.

No backend to install
API already online
Authentication endpoints ready
Perfect for learning
Workshop 01 : Tester L’API
Connect to [Link], get a free key
Install the Thunder extension in VS Code
Test a GET command to receive the list of users
CORRECTION
[Link]
header : x-api-key
value : reqres-free-v1
Bonus Other Test APIs

URL Usage

RegRes [Link] Users, auth

JSONPlaceholder [Link] Posts, comments

The Dog API [Link] Pictures of dogs

Random User [Link] Random profiles


What is useEffect?
Definition
useEffect is a React hook that allows code to be executed after a component has
rendered. It is used to manage side effects such as:

Retrieve data from an API


Subscribe to events
Modify the DOM directly
Start timers
Record analytics
What is it for?

useEffect replaces the old class component


lifecycle methods:

componentDidMount (mount)

componentDidUpdate (update)

componentWillUnmount (unmount)
Basic Syntax
import { useEffect } from 'react';

useEffect (() => {


// Code to execute after rendering
[Link]('Component displayed!');
}, [dependencies]); // Dependency table

Two parameters:

1. Callback function (required) - The code to execute

2. Dependency Table (optional) - Controls when to execute


Example 1
useEffect can return a function that will be executed:
Before the next execution of the effect
When the component is disassembled

Why? To avoid memory leaks.


Example 2
ATTENTION !
ATTENTION !
setInterval: Call a function repeatedly with a certain interval (in
milliseconds) between each call.

setInterval returns an identifier that can be given to clearInterval to


stop the repetition.

The identifiers for setInterval and setTimeout are of the same type,
but it is recommended to use clearInterval for intervals and
clearTimeout for timers.

The time limit is increased to a 32-bit integer, resulting in a maximum


of approximately 24 days.
Atelier 02 : UseEffect
Développer un code pour récupérer la liste d’utilisateurs d’un
API puis l’afficher dans le retour du composant Users
Ajouter un filtre pour filtrer les Utilisateurs
Workshop 02 : UseEffect
Develop code to retrieve the list of users from an API and then
display it in the return value of the Users component.
Add a filter to filter users
Correction
useEffect(() => {
fetch('[Link] {
headers: { const [users, setUsers] = useState([]);
'x-api-key': 'reqres-free-v1'
}
}) <ul>
.then(response => { {[Link](user => (
if (![Link]) { <li key={[Link]}>{user.first_name}</li>
throw new Error('Erreur de réseau'); ))}
} </ul>
return [Link]();
})
.then(data => setUsers([Link] || []))
.catch(() => setUsers([]));
}, []);
const [searchTerm, setSearchTerm] = useState('');

recherche : <input
type="text"
value={searchTerm}
onChange={e => setSearchTerm([Link])}
placeholder="Rechercher..."
/>

const filteredUsers = [Link](user =>


user.first_name.toLowerCase().includes([Link]())
);
If you want to optimize (avoid unnecessary
recalculation), you can use useMemo:
This will recalculate filteredUsers only when users or searchTerm
change, otherwise it reuses the previous value.
useMemo.
`useMemo` is a React hook that allows you to memorize (cache) the result of
a calculation to avoid repeating it unnecessarily each time the component is
rendered. This is useful for optimizing performance when the calculation is
computationally expensive.
The function passed to useMemo is only executed when one of the
dependencies changes.
If the dependencies are always the same, React uses the stored value without
recalculating.
The [dependencies] table lists the variables on which the calculation depends.
When to choose which one?
Use useEffect if you want to do something after React has
rendered the component, especially to interact with the outside
(API, DOM, timers...).

Use useMemo to store a calculated value that depends on data


and avoid repeating this calculation on each render, which can
improve performance.
Workshop 03 : UseMemo
Use Usememo with the filter rendering (FiltredUsers)
Correction
// Store the filtered list: recalculate only if users or searchTerm change

const filteredUsers = useMemo(() => { return [Link](user =>


user.first_name.toLowerCase().includes([Link]()) );
}, [users, searchTerm]);
Astuce : AWAIT vs ASYNC
1. async = function that returns a Promise
2. await = waits for the result of a Promise
3. try/catch = handles errors

Note: await only works within an async function.


Astuce : AWAIT vs ASYNC
What is a Promise?

A Promise is a JavaScript object that represents the future


result of an asynchronous operation. It's like a "promise"
that a result will be available later, whether it's a success or
a failure.

A Promise can be in one of these 3 states:


1. Pending - Initial state, the operation is in progress
2. Fulfilled (Resolved) - The operation was successful; a
result was obtained.
3. Rejected - The operation failed; an error occurred.
❌ AVOID
`await` in a normal
function (syntax error)
Loops with await
Forget about error
handling

✅ DO
Always use try/catch to handle errors
Use [Link] for independent requests
Declare the function async before using await
You can use `await` as many times as you want in an async
function. It's actually very common and perfectly normal!
Each await pauses the function's execution until the promise is
resolved, then moves on to the next await. It's like a queue: they
execute one after the other (sequentially).
fetch() executes the request and returns a promise.
response is the object that responds to the request.
then() handles success.
catch() handles errors.
Then and try/catch are two different ways to handle
asynchronous operations and errors in JavaScript, but they are
used in different contexts:

.then: handles the case where the promise is successfully resolved.


.catch: handles errors or rejections of the promise.

=> Chained syntax for handling successes and errors.


=> Definitive functions for handling the response to a promise.

Use .then().catch() if you are working with promises without


async/await.

Use async/await + try/catch for a more readable and


synchronous code style that handles
Local
Storage
Storage in localStorage is secured by the Same-Origin policy: a web page
cannot access the storage of another domain.
In summary, localStorage is very useful for saving user preferences,
authentication tokens, or any data you want to keep between user sessions in
the browser.
localStorage and cookies have different and complementary roles. Choose according to your
storage, security, and client/server transmission needs.
Local Storage Management To check the data saved by your site (e.g., tokens,
preferences):
Interface: (F12) > Application or Storage tab > Local Storage section.
Console: Type [Link](localStorage) for a quick view in table format.
Workshop 04 : UseEffect
Update the title when searchTerm changes with useEffects
Save the State searchterm to localStorage
When closing the component, remove searchterm from
localstorage
Correction
// Effect 2 : Mettre à jour le titre quand searchTerm change

useEffect(() => {
[Link] = `Recherche : ${searchTerm}`;
}, [searchTerm]);
Correction
// Effect 3 : Sauvegarder dans localStorage
useEffect(() => { if (searchTerm) {
[Link]('lastSearch', searchTerm); } }, [searchTerm]);

// Effect 3 : Supprimer localStorage dans return de useEffect


[Link]('searchTerm');
Session Storage
The data only lives during the tab/window session.
They survive refreshing (Ctrl+R / F5), but are deleted as soon
as the tab or window is closed.
Not shared with other tabs, even if it's the same site: each tab
has its own sessionStorage.
Similar capacity (≈5 MB).
Recap

localStorage: common to all tabs/windows of the same


origin. If you log into a tab and save a token, the other
tabs will also see that token.

SessionStorage: specific to each tab. You can open the


same website in two tabs, having two independent
"sessions" (e.g., two different bookings in parallel).
Authentication
In React, the login principle is
primarily a matter of flow: form→
API call→ storage of the
"connected" state → page protection
→ logout.

React does not perform


authentication itself; it manages the
interface and state around an API
(backend, external service, etc.).
React login steps

Login form
You display a <Login /> component with controlled fields
(email, password) managed by useState.
At onSubmit, you prevent the page from reloading and you
trigger a handleLogin function.

Call to an authentication API


handleLogin sends the credentials to an endpoint
(e.g., POST /login or POST [Link]
using fetch or axios.
The backend checks the credentials, then returns
either a success (token, user info) or an error (401,
400, error message).
React login steps

Authentication status update


If successful, save the important information:
dans le state React (ex: user, isAuthenticated, token), via
useState ou Context ou Redux.
possibly in localStorage or sessionStorage to persist
between refresh/tab closure.
If there is an error, you display a message (e.g.,
“Incorrect email or password”).

Navigation after login


Once connected, you redirect the user to a protected
area (e.g., /dashboard), using useNavigate (React
Router) or by conditionally rendering another
component.
Overall authentication management within the app

Source of truth: “auth state” You centralize the authentication


state (e.g., isAuthenticated + token) in:
An AuthContext (Context API) This state is used for:
to know if the user is logged in,
customize the UI (display “Log out”, username),
attach the token in the headers of the API requests (e.g.,
Authorization: Bearer ...).
Session persistence and reloading

On the first render of the app, you read localStorage /


sessionStorage to retrieve any token already stored.
If found, you rebuild the authentication state (the user is directly
“connected” after refresh).
If absent or invalid, you redirect it to the login page.
Overall authentication management within the app

Logout

The “Log out” button:


removes the token from the state and storage.
optionally calls a /logout endpoint on the backend.
redirects the user to /login.
Page protection (protected routes)

Concept

A “protected route” is a page that should only be visible if the


user is logged in (e.g., /dashboard, /profile).
On the React Router side, you create a component that
checks the auth status:
If logged in→ displays the protected page.
otherwise → redirects to /login.
Hook : UseLocation
`useLocation` is a hook
provided by React Router that
gives you all the information
about the current URL (path,
query string, hash, navigation
state, etc.). It is only used in a
component rendered inside a
Router.
useLocation() returns a location object that looks like this:
pathname: le chemin (ex: /admin/users)

search: la query string (ex: '?page=2&filter=active').


hash: the part after # if there is one (ex: '#section1').
state: an optional object used to pass data between
pages without putting it in the URL (useful for "from",
messages, etc.)
Typical use with auth

In your
protected
route, you pass
the location in
the redirection
state:
On the Login page, you retrieve this information to
know where to redirect the user after successful login:
Workshop 05 : LOGIN
Add a LOGIN component to connect to the administrative section
Add code to ensure secure browsing in case of connection
Correction: [Link]
Check if a token is already registered
const token = [Link]('authToken'); const
isAuthenticated = Boolean(token);
const location = useLocation();

Add the Login component route


{/* Auth */} <Route path="/login" element={
isAuthenticated ? ( <Navigate to="/admin" replace /> ) : (
<Login /> ) } />
Correction: [Link]
If a user is not logged in => redirect to /login
<ProtectedRoute isAllowed={isAuthenticated} redirectPath="/login" >
Correction: [Link] (1)
// Pages/[Link]
import { useState } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';

function Login() {
const [email, setEmail] = useState('[Link]@[Link]');
const [password, setPassword] = useState('cityslicka');
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);

const navigate = useNavigate();


const location = useLocation();
const from = [Link]?.from?.pathname || '/admin';

async function handleSubmit(e) {


[Link]();
setError(null);
setLoading(true);
Correction: [Link] (2)
/ try {
const res = await fetch('[Link] {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: [Link]({ email, password }),
});
const data = await [Link]();
if (![Link]) {
throw new Error([Link] || 'Erreur de connexion');
}
[Link]('authToken', [Link]);
navigate(from, { replace: true });
} catch (err) {
setError([Link]);
} finally {
setLoading(false);
}
}
Correction: [Link] (3)
return (
<div className="min-h-screen flex items-center justify-center bg-gray-100 px-4">
<div className="w-full max-w-md bg-white rounded-xl shadow-lg px-8 py-6">
<h1 className="text-2xl font-semibold text-gray-900">
Connexion
</h1>
<p className="mt-1 text-sm text-gray-500">
Connectez-vous pour accéder à l&apos;espace administration.
</p>

{error && (
<div className="mt-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700">
{error}
</div>
)}

<form onSubmit={handleSubmit} className="mt-6 space-y-4">


<div>
<label className="block text-sm font-medium text-gray-700">
Adresse e-mail
</label>
<input
type="email"
autoComplete="email"
value={email}
onChange={(e) => setEmail([Link])}
className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:outline-none
focus:ring-2 focus:ring-indigo-500"
placeholder="vous@[Link]"
/>
Correction: [Link] (4)
</div>
<div>
<label className="block text-sm font-medium text-gray-700">
Mot de passe
</label>
<input
type="password"
autoComplete="current-password"
value={password}
onChange={(e) => setPassword([Link])}
className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:outline-none
focus:ring-2 focus:ring-indigo-500"
placeholder="••••••••"
/>
</div>
<button
type="submit"
disabled={loading}
className="w-full mt-2 inline-flex items-center justify-center rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-medium text-white shadow-sm hover:bg-indigo-700
focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-60"
>
{loading ? 'Connexion…' : 'Se connecter'}
</button>
<p className="mt-3 text-center text-xs text-gray-500">
Email de test: [Link]@[Link] — Mot de passe: cityslicka
</p>
</form>
</div>
</div>
);} export default Login;
Correction: [Link]
A component for disconnecting
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

function Logout() { const navigate = useNavigate();

useEffect(() => { [Link]('authToken');


navigate('/login', { replace: true });
}, [navigate]);

return null;
}
Correction: [Link]
Add the route to this component
<Route path="/logout" element={<Logout />} />
Correction: [Link]
fix the disconnect button
<Link to="/logout" className="inline-flex w-full items-center
justify-center rounded-md border border-gray-300 bg-white px-3
py-1.5 text-sm font-medium text-gray-700 shadow-sm hover:bg-
gray-50" > Se déconnecter </Link>
Correction: [Link] (1)
import React, { useEffect, useState } from 'react';
import { Link, useNavigate, useLocation } from 'react-router-dom';
import { FaBars, FaTimes } from 'react-icons/fa';

const Navbar = () => {


const [isMobileOpen, setIsMobileOpen] = useState(false);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const navigate = useNavigate();
const location = useLocation();

// Vérifier le token au chargement et à chaque changement de route


useEffect(() => {
const token = [Link]('authToken');
setIsAuthenticated(Boolean(token));
}, [[Link]]);

const navigation = [
{ name: 'Accueil', path: '/' },
{ name: 'À propos', path: '/about' },
{ name: 'Expérience', path: '/experience' },
{ name: 'Contact', path: '/contact' },
];
Correction: [Link] (2)
i function handleLogout() {
[Link]('authToken');
navigate('/login', { replace: true });
}

return (
<nav className="bg-gray-900 sticky top-0 z-50 shadow-lg">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="flex h-16 items-center justify-between">
{/* Logo */}
<div className="flex items-center">
<Link to="/" className="flex items-center gap-3">
<div className="w-9 h-9 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-lg flex items-center
justify-center text-white font-bold text-lg shadow-lg">
21C
</div>
<span className="hidden sm:inline text-white font-semibold text-lg">
21C Digital
</span>
</Link>
</div>
Correction: [Link] (3)
{/* Desktop navigation */}
<div className="hidden md:flex md:items-center md:gap-8">
<div className="flex items-center gap-4">
{[Link]((item) => (
<Link key={[Link]}
to={[Link]}
className={`text-sm font-medium transition ${
[Link] === [Link]
? 'text-white border-b-2 border-indigo-500 pb-1'
: 'text-gray-300 hover:text-white'
}`}
> {[Link]} </Link> ))} </div>
{/* Zone Auth conditionnelle */}
<div className="flex items-center gap-3 ml-6">
{!isAuthenticated ? (
// Non connecté -> bouton Se connecter
<Link to="/login"
className="rounded-full bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm
hover:bg-indigo-700 transition" > Se connecter </Link>
):( // Connecté -> lien Dashboard + bouton Déconnexion
<> <Link to="/admin" className="text-sm font-medium text-gray-200 hover:text-white
transition" > Tableau de bord </Link>
Correction: [Link] (4)
<button type="button" onClick={handleLogout}
className="rounded-full border border-gray-500 px-4 py-2 text-sm font-medium text-gray-200 hover:bg-
gray-800 transition" > Se déconnecter </button>
</> )} </div> </div>
{/* Mobile button */}
<div className="flex md:hidden">
<button
onClick={() => setIsMobileOpen(!isMobileOpen)}
type="button"
className="inline-flex items-center justify-center rounded-md p-2 text-gray-300 hover:bg-gray-800
hover:text-white transition"
>
<span className="sr-only">Ouvrir le menu</span>
{isMobileOpen ? (
<FaTimes className="h-6 w-6" />
):(
<FaBars className="h-6 w-6" />
)} </button> </div> </div>
</div>
Correction: [Link] (5)
{/* Mobile menu */}
{isMobileOpen && (
<div className="md:hidden bg-gray-900 border-t border-gray-800">
<div className="space-y-1 px-4 pb-3 pt-2">
{[Link]((item) => (
<Link
key={[Link]}
to={[Link]}
className={`block rounded-md px-3 py-2 text-base font-medium transition ${
[Link] === [Link]
? 'bg-gray-800 text-white'
: 'text-gray-300 hover:bg-gray-800 hover:text-white'
}`}
onClick={() => setIsMobileOpen(false)}
> {[Link]} </Link> ))} </div>
<div className="border-t border-gray-800 px-4 py-3">
{!isAuthenticated ? (
<Link to="/login"
className="block w-full text-center rounded-full bg-indigo-600 px-4 py-2 text-sm font-medium text-white
shadow-sm hover:bg-indigo-700 transition"
onClick={() => setIsMobileOpen(false)} > Se connecter </Link> ) : ( <div className="flex flex-col
gap-2">
Correction: [Link] (6)
<Link
to="/admin"
className="block w-full text-center rounded-full bg-gray-800 px-4 py-2 text-sm font-medium text-gray-100
hover:bg-gray-700 transition"
onClick={() => setIsMobileOpen(false)}
>
Tableau de bord
</Link>
<button
type="button"
onClick={() => {
setIsMobileOpen(false);
handleLogout();
}}
className="block w-full rounded-full border border-gray-500 px-4 py-2 text-sm font-medium text-gray-200
hover:bg-gray-800 transition"
>
Se déconnecter
</button>
</div>
)}
</div>
</div> )} </nav> );};
export default Navbar;
AXIOS
To “verify” an access token, two
things must be distinguished:

Check on the client side if a


token exists/is still valid for
the UI.
Verify on the server side that
the token is truly valid
(signature, expiration). The
real security check always
happens on the backend, not
in React.
We can implement a specific pattern that covers:
token verification on app load / route change
Automatic addition of the token to requests
reaction when the backend responds with a 401 (automatic logout )
It is a JWT (JSON Web Token) composed of 3 parts encoded in Base64URL:
Header: a small JSON file that indicates the token type and the signature
algorithm
Payload: JSON containing information (claims) about the user or session. It is not
encrypted, just encoded.
Signature: the result of a hash/signature function (e.g., HMACSHA256) applied
to [Link] with a secret, allowing the server to verify that the token has
not been modified and, with exponentially, that it is still valid.
1 - Verify the token in the global state (auth context )

Creates an AuthContext that reads the token at startup and exposes


isAuthenticated, token, login, logout.
At mount: read [Link]('authToken').
If found → isAuthenticated = true.
If not found → false.

2 - Check (client-side) the expiration if you are using a JWT


If your token is a JWT with an exp field, you can decode it (without verifying the
signature) to see if it has expired.
Decode the payload (atob in the central part ).
Read exp (timestamp in seconds).
If [Link]() / 1000 > exp → consider the token as expired⇒ logout() on the
client side.

Important: this does not replace server-side verification, but it is useful for UX
(avoiding leaving a user "visually" connected with a dead token).
3- Check each API request via the backend API

This is the real verification: with each call to your backend:


You add the token in the Authorization: Bearer <token> header
The server decodes, checks the signature/expiration and
responds:
200 → token OK, gives the data.
401/403 → invalid/expired token, the UI must perform a logout.
4- Verify access to each page (protected routes)

With React Router, you continue what you've already set up:
Your ProtectedRoute reads the auth state (isAuthenticated or token from the context or
localStorage),
If there is no token → redirection to /login
You can add a refresh token system (short token + refresh via httpOnly cookie), but that's getting
into an "advanced" level.
The system generates a refresh token upon login (after successful authentication).
It is reused later to request new access tokens each time the access token expires, until the
refresh token itself expires or is revoked.
In summary

Front:
always read the token from localStorage in an AuthContext or at the App level, use
this token to condition the display (navbar, routes, etc.), and put it in the
Authorization header of each request via an API layer.

Back (when you migrate from requests to your .NET/Node API):


check the token on each protected endpoint, respond 401 → and on the React
side, trigger an automatic logout.
Exemple
d’Utilisation
Exemple
d’Utilisation
Exemple d’Utilisation
Workshop 6: Installing and
testing axios with the same API
// Add an authentication token const api = [Link]({
baseURL: '[Link] headers: { 'Authorization':
`Bearer ${[Link]('token')}`, 'Content-Type':
'application/json' } });

// Utilisation [Link]('/users').then(response =>


[Link]([Link]));
[Link]('/users', { name: 'John' });
// src/api/[Link]
import axios from "axios";

correction const api = [Link]({


baseURL: "[Link] // ou ton backend: "[Link]
headers: {
"Content-Type": "application/json",
1 - installer axios },
"x-api-key": "reqres_6f885693614d4c249543cdfc0ea56f72",

npm install axios });

// Intercepteur pour ajouter le token si tu utilises l’auth


[Link](
(config) => {
const token = [Link]("authToken"); // adapte à ton storage
if (token) {
[Link] = `Bearer ${token}`;
}
return config;
},
(error) => [Link](error)
);

// Intercepteur de réponse (gestion globale des erreurs / 401, etc.)


[Link](
(response) => response,
(error) => {
if ([Link] && [Link] === 401) {
// ex: rediriger vers /login ou nettoyer le storage
// [Link]("authToken");
}
return [Link](error);
}
);

export default api;


TRICK
An interceptor (in React, this is often referred to as Axios interceptors) is a
function that sits in the middle of all your HTTP requests: it can modify the
request before it's sent, and/or the response before it reaches your code.

For Axios, for example:


Request interceptor: called just before a request is sent.
Add the Authorization: Bearer <token> header to all requests.
add common headers (language, version, etc.),
Log the URLs called.
Response interceptor: called as soon as a response is received.
handle 401/403 errors globally (automatic logout if token is invalid),
display an error toast,
measure response time,
transform the data before returning it to your code.
correction
2. Utiliser l’instance dans les pages

useEffect(() => {
const fetchUsers = async () => {
try { const response = await [Link]("/users");
// baseURL + "/users"
setUsers([Link] || []); }
catch (error) {
[Link]("Erreur de réseau", error);
setUsers([]); }
};
fetchUsers(); }, []);
3. Variant: resource-based services
// src/services/[Link]

import api from ".. /api/axios";


export const getUsers = () => [Link]("/users");
export const getUser = (id) => [Link](`/users/${id}`);
export const createUser = (payload) => [Link]("/users",
payload);
export const updateUser = (id, payload) => [Link](`/users/${id}`,
payload);
export const deleteUser = (id) => [Link](`/users/${id}`);
import { getUsers } from ".. /.. /services/userService";

useEffect(() => {
const fetchUsers = async () => {
try {
const res = await getUsers();
setUsers([Link] || []);
} catch (e) {
setUsers([]);
}
};
fetchUsers();
}, []);

You might also like