? Notes sur la boîte à outils Redux ?
Qu'est-ce que Redux ?
Redux est un conteneur d'état flexible pour les applications JS qui gère l'état de notre application séparément. Il gère l'état de l'application dans un seul magasin, ce qui facilite la gestion d'une logique d'état complexe dans l'ensemble de l'application.
Pourquoi Redux ?
Dans un flux normal, nous devons effectuer un forage d'hélices pour transmettre les états entre les composants. Certains niveaux n’ont pas besoin des États ici, ce qui constitue un fardeau. De plus, améliorer l’état des applications de grande taille n’est pas une solution évolutive car cela nécessite des changements structurels. C'est pourquoi nous avons besoin d'un redux pour gérer les états. Tous les états ici sont conservés en magasin et quel que soit le composant qui en a besoin, ils peuvent simplement s'abonner à ce magasin. Redux garantit une gestion prévisible de l'état, un débogage plus facile et une évolutivité améliorée en appliquant un flux de données unidirectionnel.
Composants de base Redux :
Action : Un objet qui décrit ce qui s'est passé. Il contient généralement un type et une charge utile facultative. (Une commande)
Dispatch : Une fonction utilisée pour envoyer des actions au magasin pour mettre à jour l'état. (Un événement survenant)
Réducteur : Une fonction pure qui prend l'état actuel et une action, puis renvoie un nouvel état. (Fonction qui se déclenche lors de l'envoi d'une action)
Installation : npm i @reduxjs/toolkit réagir-redux
Flux de travail Redux :
Créer une tranche :
Une tranche est un ensemble de logiques et d'actions de réduction Redux pour une seule fonctionnalité. Le rappel de préparation nous permet de personnaliser la charge utile de l'action avant qu'elle n'atteigne le réducteur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import { createSlice, nanoid } from "@reduxjs/toolkit" ;
const postSlice = createSlice({
name: "posts" ,
initialState: [],
reducers: {
addPost: {
reducer: (state, action) => {
state.push(action.payload);
},
prepare: (title, content) => ({
payload: { id: nanoid(), title, content },
}),
},
deletePost: (state, action) => {
return state.filter((post) => post.id != action.payload);
},
},
});
export const { addPost, deletePost } = postSlice.actions;
export default postSlice.reducer;
|
Copier après la connexion
Copier après la connexion
Création de boutique :
1 2 3 4 5 6 7 8 | import { configureStore } from "@reduxjs/toolkit" ;
import postReducer from "../features/posts/postSlice" ;
export const store = configureStore({
reducer: {
posts: postReducer
},
});
|
Copier après la connexion
Copier après la connexion
Enveloppement avec le fournisseur :
1 2 3 4 5 6 7 8 9 10 | import { Provider } from "react-redux" ;
import { store } from "./app/store.jsx" ;
createRoot(document.getElementById( "root" )).render(
<StrictMode>
<Provider store={store}>
<App />
</Provider>
</StrictMode>
);
|
Copier après la connexion
Copier après la connexion
Utilisation dans le composant :
1 2 3 4 5 6 7 8 9 10 11 12 | const PostList = ({ onEdit }) => {
const posts = useSelector((state) => state.posts);
const dispatch = useDispatch();
return (
<div className= "w-full grid grid-cols-1 gap-6 mt-12" >
{posts.map((post) => (
<div key={post.id}></div>
))}
</div>
);
};
|
Copier après la connexion
Copier après la connexion
Extension du navigateur Redux : Redux DevTools
1 2 3 4 | const store = configureStore({
reducer: rootReducer,
devTools: process.env.NODE_ENV !== 'production' ,
});
|
Copier après la connexion
Copier après la connexion
Opération asynchrone dans Redux (Redux Thunk) :
Dans Redux, les opérations asynchrones (comme les appels d'API) sont gérées à l'aide d'un middleware car Redux ne prend en charge par défaut que les mises à jour d'état synchrones. Les middlewares les plus courants pour gérer les opérations asynchrones sont Redux Thunk, Redux Toolkit (RTK) avec createAsyncThunk et Redux Saga.
Mise en œuvre :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' ;
export const fetchPosts = createAsyncThunk( 'posts/fetchPosts' , async () => {
const response = await fetch( 'https://jsonplaceholder.typicode.com/posts' );
return response.json();
});
const initialState = {
posts: [],
post: null,
loading: false,
error: null,
};
const postsSlice = createSlice({
name: 'posts' ,
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchPosts.pending, (state) => {
state.loading = true;
})
.addCase(fetchPosts.fulfilled, (state, action) => {
state.loading = false;
state.posts = action.payload;
})
.addCase(fetchPosts.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
})
},
});
export default postsSlice.reducer;
|
Copier après la connexion
Copier après la connexion
Cas d'utilisation :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import React, { useEffect } from 'react' ;
import { useSelector, useDispatch } from 'react-redux' ;
import { fetchPosts, createPost, updatePost, deletePost } from './postsSlice' ;
const Posts = () => {
const dispatch = useDispatch();
const { posts, loading, error } = useSelector((state) =>state.posts);
useEffect(() => {
dispatch(fetchPosts());
}, [dispatch]);
const handleCreate = () => {
dispatch(createPost({ title: 'New Post' , body: 'This is a new post' }));
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<div>
<h1>Posts</h1>
<button onClick={handleCreate}>Create Post</button>
</div>
);
};
export default Posts;
|
Copier après la connexion
Copier après la connexion
Middleware
Le middleware de Redux intercepte les actions distribuées, permettant la journalisation, le rapport d'incident ou la gestion de la logique asynchrone. Le middleware nous permet de personnaliser le processus d'envoi.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | const blogPostMiddleware = (storeAPI) => (next) => (action) => {
if (action.type === 'posts/publishPost' ) {
const contentLength = action.payload.content.length;
if (contentLength < 50) {
console.warn( 'Post content is too short. Must be at least 50 characters.' );
return ;
}
console.log( 'Publishing post:' , action.payload.title);
}
return next(action);
};
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(blogPostMiddleware),
});
|
Copier après la connexion
Copier après la connexion
Sélecteurs
Les sélecteurs aident à accéder à des parties spécifiques de l’État.
export const selectCount = (state) => état.compteur.valeur;
Gestion des erreurs
Gérez efficacement les erreurs grâce à une gestion appropriée de l'état.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import { createSlice, nanoid } from "@reduxjs/toolkit" ;
const postSlice = createSlice({
name: "posts" ,
initialState: [],
reducers: {
addPost: {
reducer: (state, action) => {
state.push(action.payload);
},
prepare: (title, content) => ({
payload: { id: nanoid(), title, content },
}),
},
deletePost: (state, action) => {
return state.filter((post) => post.id != action.payload);
},
},
});
export const { addPost, deletePost } = postSlice.actions;
export default postSlice.reducer;
|
Copier après la connexion
Copier après la connexion
Requête RTK (récupération de données simplifiée)
RTK Query simplifie la récupération, la mise en cache et la synchronisation des données. RTK Query met automatiquement en cache les requêtes et évite les récupérations inutiles, améliorant ainsi les performances.
Configuration de la requête RTK
1 2 3 4 5 6 7 8 | import { configureStore } from "@reduxjs/toolkit" ;
import postReducer from "../features/posts/postSlice" ;
export const store = configureStore({
reducer: {
posts: postReducer
},
});
|
Copier après la connexion
Copier après la connexion
Utilisation dans les composants
1 2 3 4 5 6 7 8 9 10 | import { Provider } from "react-redux" ;
import { store } from "./app/store.jsx" ;
createRoot(document.getElementById( "root" )).render(
<StrictMode>
<Provider store={store}>
<App />
</Provider>
</StrictMode>
);
|
Copier après la connexion
Copier après la connexion
Mises à jour immuables avec Immer
Immer nous permet d'écrire une logique qui "mute" l'état directement tout en gardant les mises à jour immuables sous le capot.
1 2 3 4 5 6 7 8 9 10 11 12 | const PostList = ({ onEdit }) => {
const posts = useSelector((state) => state.posts);
const dispatch = useDispatch();
return (
<div className= "w-full grid grid-cols-1 gap-6 mt-12" >
{posts.map((post) => (
<div key={post.id}></div>
))}
</div>
);
};
|
Copier après la connexion
Copier après la connexion
Mutation vs Immuable
Muter : Modifier les données directement. Par exemple, modifier un objet ou un tableau.
Immuable : Au lieu de modifier directement les données, nous créons une nouvelle copie avec les modifications appliquées, laissant les données d'origine intactes.
Comment fonctionne Immer
Immer nous aide à écrire du code qui donne l'impression que nous sommes en train de muter des données (c'est-à-dire que nous les modifions directement), mais il maintient automatiquement les modifications immuables sous le capot. Ceci est utile pour éviter les bugs courants liés aux structures de données immuables en JavaScript.
Exemple : Sans Immer (mutation) :
1 2 3 4 | const store = configureStore({
reducer: rootReducer,
devTools: process.env.NODE_ENV !== 'production' ,
});
|
Copier après la connexion
Copier après la connexion
Avec Immer (immuabilité) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' ;
export const fetchPosts = createAsyncThunk( 'posts/fetchPosts' , async () => {
const response = await fetch( 'https://jsonplaceholder.typicode.com/posts' );
return response.json();
});
const initialState = {
posts: [],
post: null,
loading: false,
error: null,
};
const postsSlice = createSlice({
name: 'posts' ,
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchPosts.pending, (state) => {
state.loading = true;
})
.addCase(fetchPosts.fulfilled, (state, action) => {
state.loading = false;
state.posts = action.payload;
})
.addCase(fetchPosts.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
})
},
});
export default postsSlice.reducer;
|
Copier après la connexion
Copier après la connexion
Cela facilite le travail avec Redux (ou toute autre gestion d'état) car nous n'avons pas besoin de cloner et de mettre à jour l'état manuellement ; Immer le fait automatiquement pour nous.
Redux persiste :
Pour conserver l'état Redux lors des actualisations de page, nous pouvons intégrer Redux Persist. Cela stockera votre état Redux dans le stockage local ou le stockage de session et le rechargera lorsque l'application sera actualisée.
Installer :
npm installer redux-persist
Mettre en œuvre :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import React, { useEffect } from 'react' ;
import { useSelector, useDispatch } from 'react-redux' ;
import { fetchPosts, createPost, updatePost, deletePost } from './postsSlice' ;
const Posts = () => {
const dispatch = useDispatch();
const { posts, loading, error } = useSelector((state) =>state.posts);
useEffect(() => {
dispatch(fetchPosts());
}, [dispatch]);
const handleCreate = () => {
dispatch(createPost({ title: 'New Post' , body: 'This is a new post' }));
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<div>
<h1>Posts</h1>
<button onClick={handleCreate}>Create Post</button>
</div>
);
};
export default Posts;
|
Copier après la connexion
Copier après la connexion
Enrouler avec Persisit Gate :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | const blogPostMiddleware = (storeAPI) => (next) => (action) => {
if (action.type === 'posts/publishPost' ) {
const contentLength = action.payload.content.length;
if (contentLength < 50) {
console.warn( 'Post content is too short. Must be at least 50 characters.' );
return ;
}
console.log( 'Publishing post:' , action.payload.title);
}
return next(action);
};
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(blogPostMiddleware),
});
|
Copier après la connexion
Copier après la connexion
Améliorations facultatives
Utilisez sessionStorage au lieu de localStorage :
Changez le stockage en basé sur la session (s'efface à la fermeture du navigateur) :
1 2 3 4 5 6 7 8 9 10 | initialState: {
items: [],
status: 'idle' ,
error: null,
},
.addCase(fetchData.rejected, (state, action) => {
state.status = 'failed' ;
state.error = action.error.message;
});
|
Copier après la connexion
Persistance sélective :
Ne conserver que des tranches spécifiques de l'État :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' ;
const api = createApi({
reducerPath: 'api' ,
baseQuery: fetchBaseQuery({ baseUrl: 'https://jsonplaceholder.typicode.com' }),
endpoints: (builder) => ({
getPosts: builder.query({
query: () => '/posts' ,
}),
getPostById: builder.query({
query: (id) => `/posts/${id}`,
}),
createPost: builder.mutation({
query: (newPost) => ({
url: '/posts' ,
method: 'POST' ,
body: newPost,
}),
}),
updatePost: builder.mutation({
query: ({ id, ...updatedPost }) => ({
url: `/posts/${id}`,
method: 'PUT' ,
body: updatedPost,
}),
}),
deletePost: builder.mutation({
query: (id) => ({
url: `/posts/${id}`,
method: 'DELETE' ,
}),
}),
}),
});
export const {
useGetPostsQuery,
useGetPostByIdQuery,
useCreatePostMutation,
useUpdatePostMutation,
useDeletePostMutation,
} = api;
export default api;
|
Copier après la connexion
J'ai créé un projet de blog simple avec un design React, Redux et Ant ayant des fonctionnalités CRUD. Vous pouvez le vérifier.
Lien du projet - Application de blog Redux
? Maîtrisez Redux Toolkit et améliorez vos applications React !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!