Maison >interface Web >js tutoriel >Notes complètes sur la boîte à outils Redux pour les développeurs React
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.
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
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.
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;
Création de boutique :
import { configureStore } from "@reduxjs/toolkit"; import postReducer from "../features/posts/postSlice"; export const store = configureStore({ reducer: { posts: postReducer }, });
Enveloppement avec le fournisseur :
import { Provider } from "react-redux"; import { store } from "./app/store.jsx"; createRoot(document.getElementById("root")).render( <StrictMode> <Provider store={store}> <App /> </Provider> </StrictMode> );
Utilisation dans le composant :
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> ); };
Extension du navigateur Redux : Redux DevTools
const store = configureStore({ reducer: rootReducer, devTools: process.env.NODE_ENV !== 'production', });
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 :
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; // Fetch all posts export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => { const response = await fetch('https://jsonplaceholder.typicode.com/posts'); return response.json(); }); // Initial State const initialState = { posts: [], post: null, loading: false, error: null, }; // Slice const postsSlice = createSlice({ name: 'posts', initialState, reducers: {}, extraReducers: (builder) => { builder // Fetch all posts .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;
Cas d'utilisation :
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;
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.
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), });
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.
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;
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
import { configureStore } from "@reduxjs/toolkit"; import postReducer from "../features/posts/postSlice"; export const store = configureStore({ reducer: { posts: postReducer }, });
Utilisation dans les composants
import { Provider } from "react-redux"; import { store } from "./app/store.jsx"; createRoot(document.getElementById("root")).render( <StrictMode> <Provider store={store}> <App /> </Provider> </StrictMode> );
Immer nous permet d'écrire une logique qui "mute" l'état directement tout en gardant les mises à jour immuables sous le capot.
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> ); };
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) :
const store = configureStore({ reducer: rootReducer, devTools: process.env.NODE_ENV !== 'production', });
Avec Immer (immuabilité) :
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; // Fetch all posts export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => { const response = await fetch('https://jsonplaceholder.typicode.com/posts'); return response.json(); }); // Initial State const initialState = { posts: [], post: null, loading: false, error: null, }; // Slice const postsSlice = createSlice({ name: 'posts', initialState, reducers: {}, extraReducers: (builder) => { builder // Fetch all posts .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;
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.
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 :
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;
Enrouler avec Persisit Gate :
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), });
Utilisez sessionStorage au lieu de localStorage :
Changez le stockage en basé sur la session (s'efface à la fermeture du navigateur) :
initialState: { items: [], status: 'idle', error: null, }, .addCase(fetchData.rejected, (state, action) => { state.status = 'failed'; state.error = action.error.message; });
Persistance sélective :
Ne conserver que des tranches spécifiques de l'État :
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;
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!