Dans ce guide, nous aborderons :
- Opérations CRUD
- Pagination
- Redux persiste avec la requête RTK
- Utilisation de plusieurs URL de base
- Voies protégées et publiques
- Gestion et invalidation du cache
RTK Query est un outil avancé de récupération de données et de mise en cache intégré à Redux Toolkit (RTK). Il rationalise les interactions API en générant des tranches et des hooks Redux pour les tâches courantes telles que la récupération, la mise en cache et la mise à jour des données. Les principales fonctionnalités incluent :
- Mise en cache automatique : la requête RTK met en cache les données et les récupère automatiquement lorsque les données sont invalidées, garantissant ainsi que l'interface utilisateur dispose toujours des données les plus récentes.
- Invalidation du cache : à l'aide de balises, RTK Query vous permet de définir quand certaines données doivent être récupérées. Cela permet de garder votre cache à jour sans mettre à jour manuellement les données.
- Hooks générés automatiquement : RTK Query crée des hooks pour chaque point de terminaison d'API, vous permettant d'appeler des API à l'aide de hooks React simples (useGetPostsQuery, useCreatePostMutation, etc.).
- Gestion des erreurs : inclut une gestion personnalisée des erreurs via un middleware, ce qui facilite la détection et l'affichage des erreurs.
- Intégration Redux simplifiée : RTK Query s'intègre directement à Redux, vous n'avez donc pas besoin de bibliothèques supplémentaires pour la gestion globale de l'état ou la mise en cache.
Requête RTK et requête React
React Query et RTK Query fournissent tous deux des solutions pour la récupération et la mise en cache des données dans les applications React, mais ils ont des atouts et des cas d'utilisation différents :
Feature | RTK Query | React Query |
---|---|---|
Purpose | Integrated within Redux for managing server data in Redux state. Best for apps already using Redux or requiring centralized global state. | Dedicated to managing server state with no Redux dependency. Great for apps focused on server state without Redux. |
Caching | Automatic caching with fine-grained cache invalidation through tags. Caches data globally within the Redux store. | Automatic caching with flexible cache control policies. Maintains a separate cache independent of Redux. |
Generated Hooks | Auto-generates hooks for endpoints, allowing mutations and queries using useQuery and useMutation hooks. | Provides hooks (useQuery, useMutation) that work independently from Redux, but require manual configuration of queries and mutations. |
DevTools | Integrated into Redux DevTools, making debugging seamless for Redux users. | Provides its own React Query DevTools, with detailed insight into query states and cache. |
Error Handling | Centralized error handling using Redux middleware. | Error handling within individual queries, with some centralized error-handling options. |
Redux Integration | Built directly into Redux, simplifying usage for Redux-based apps. | Not integrated with Redux by default, although Redux and React Query can be combined if needed. |
Choisir entre la requête RTK et la requête React :
-
Utiliser la requête RTK si :
- Vous utilisez déjà Redux et souhaitez une solution intégrée et rationalisée pour la récupération de données.
- Vous avez besoin d'une gestion centralisée des erreurs et de l'intégration des outils de développement dans Redux.
-
Utilisez React Query si :
- Vous souhaitez une configuration plus légère sans dépendance Redux.
- Vous préférez une gestion séparée de l'état du serveur et n'avez pas besoin de l'état global de l'application.
Essentiellement, RTK Query excelle pour les applications centrées sur Redux, tandis que React Query offre flexibilité et simplicité pour les projets sans Redux ou ceux avec une gestion plus localisée de l'état du serveur.
1. Configuration et configuration du magasin
// src/store/store.js import AsyncStorage from '@react-native-async-storage/async-storage'; import { combineReducers, configureStore, isRejectedWithValue } from '@reduxjs/toolkit'; import { setupListeners } from '@reduxjs/toolkit/query'; import { FLUSH, PAUSE, PERSIST, persistReducer, PURGE, REGISTER, REHYDRATE } from 'redux-persist'; import { authApi } from '../api/authApi'; import { postsApi } from '../api/postsApi'; import { usersApi } from '../api/usersApi'; import authSlice from '../features/auth/authSlice'; const persistConfig = { key: 'root', version: 1, storage: AsyncStorage, blacklist: ['auth', postsApi.middleware, usersApi.middleware, authApi.middleware], // these reduce will not persist data (NOTE: blacklist rtk api slices so that to use tags) // whitelist: ['users'], //these reduce will persist data }; const getEnhancers = (getDefaultEnhancers) => { if (process.env.NODE_ENV === 'development') { const reactotron = require('../reactotronConfig/ReactotronConfig').default; return getDefaultEnhancers().concat(reactotron.createEnhancer()); } return getDefaultEnhancers(); }; /** * On api error this will be called */ export const rtkQueryErrorLogger = (api) => (next) => (action) => { // RTK Query uses `createAsyncThunk` from redux-toolkit under the hood, so we're able to utilize these matchers! if (isRejectedWithValue(action)) { console.log('isRejectedWithValue', action.error, action.payload); alert(JSON.stringify(action)); // This is just an example. You can replace it with your preferred method for displaying notifications. } return next(action); }; const reducer = combineReducers({ auth: authSlice, [postsApi.reducerPath]: postsApi.reducer, [usersApi.reducerPath]: usersApi.reducer, [authApi.reducerPath]: authApi.reducer, }); const persistedReducer = persistReducer(persistConfig, reducer); const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }).concat(postsApi.middleware, usersApi.middleware, authApi.middleware, rtkQueryErrorLogger), enhancers: getEnhancers, }); setupListeners(store.dispatch); export default store;
Redux Store (src/store/store.js) : Le magasin Redux est la structure principale détenant l'état de l'application. Dans votre configuration, il est amélioré avec redux-persist pour enregistrer localement certaines parties de l'état Redux, afin qu'elles persistent même lorsque l'application redémarre.
-
redux-persist :
- Objectif : Aide à maintenir certaines parties de l'état Redux persistantes au fil des sessions d'application.
- Configuration : un objet persistConfig spécifie qu'auth, postsApi et usersApi ne doivent pas être persistants (liste noire), ce qui signifie que leurs données sont réinitialisées au redémarrage de l'application.
- persistReducer combine la configuration du réducteur avec la fonctionnalité de persistance.
Enhancers : des Enhancers personnalisés sont utilisés pour intégrer Reactotron en mode développement, un outil utile pour déboguer les actions Redux, l'état et les requêtes réseau. Cela ne s'active qu'en développement, ce qui facilite le débogage sans affecter la production.
-
Middleware :
- Les middlewares RTK Query (postsApi.middleware, usersApi.middleware, authApi.middleware) ajoutent des fonctionnalités pour la gestion automatique du cache, rendant la récupération de données efficace.
- rtkQueryErrorLogger : un middleware personnalisé enregistre les erreurs lorsque les appels d'API échouent. Il utilise la fonction isRejectedWithValue de RTK Query pour détecter et gérer les erreurs, vous permettant ainsi d'alerter les utilisateurs des problèmes ou de prendre d'autres actions.
setupListeners : Cette fonction permet la récupération automatique des données lorsque certains événements se produisent, comme lorsque l'application retrouve le focus ou reprend en arrière-plan, fournissant aux utilisateurs de nouvelles données sans actualisation manuelle.
2. Définitions API avec requête RTK
RTK Query simplifie les appels d'API en générant automatiquement des tranches Redux, des hooks et une mise en cache. Voici une répartition des API que vous avez définies :
// src/store/store.js import AsyncStorage from '@react-native-async-storage/async-storage'; import { combineReducers, configureStore, isRejectedWithValue } from '@reduxjs/toolkit'; import { setupListeners } from '@reduxjs/toolkit/query'; import { FLUSH, PAUSE, PERSIST, persistReducer, PURGE, REGISTER, REHYDRATE } from 'redux-persist'; import { authApi } from '../api/authApi'; import { postsApi } from '../api/postsApi'; import { usersApi } from '../api/usersApi'; import authSlice from '../features/auth/authSlice'; const persistConfig = { key: 'root', version: 1, storage: AsyncStorage, blacklist: ['auth', postsApi.middleware, usersApi.middleware, authApi.middleware], // these reduce will not persist data (NOTE: blacklist rtk api slices so that to use tags) // whitelist: ['users'], //these reduce will persist data }; const getEnhancers = (getDefaultEnhancers) => { if (process.env.NODE_ENV === 'development') { const reactotron = require('../reactotronConfig/ReactotronConfig').default; return getDefaultEnhancers().concat(reactotron.createEnhancer()); } return getDefaultEnhancers(); }; /** * On api error this will be called */ export const rtkQueryErrorLogger = (api) => (next) => (action) => { // RTK Query uses `createAsyncThunk` from redux-toolkit under the hood, so we're able to utilize these matchers! if (isRejectedWithValue(action)) { console.log('isRejectedWithValue', action.error, action.payload); alert(JSON.stringify(action)); // This is just an example. You can replace it with your preferred method for displaying notifications. } return next(action); }; const reducer = combineReducers({ auth: authSlice, [postsApi.reducerPath]: postsApi.reducer, [usersApi.reducerPath]: usersApi.reducer, [authApi.reducerPath]: authApi.reducer, }); const persistedReducer = persistReducer(persistConfig, reducer); const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }).concat(postsApi.middleware, usersApi.middleware, authApi.middleware, rtkQueryErrorLogger), enhancers: getEnhancers, }); setupListeners(store.dispatch); export default store;
-
authApi (src/api/authApi.js) :
- Définit une mutation de connexion qui envoie les informations d'identification de l'utilisateur (par exemple, nom d'utilisateur, mot de passe) au serveur pour s'authentifier.
- onQueryStarted : une fois la connexion réussie, il stocke le jeton renvoyé dans Redux à l'aide de l'action setToken. Cela permet des requêtes sécurisées et authentifiées vers d'autres points de terminaison.
// src/api/authApi.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { setToken } from '../features/auth/authSlice'; export const authApi = createApi({ reducerPath: 'authApi', baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com/auth/', }), endpoints: (builder) => ({ login: builder.mutation({ query: (credentials) => ({ url: 'login', method: 'POST', body: credentials, }), async onQueryStarted(arg, { dispatch, queryFulfilled }) { try { const { data } = await queryFulfilled; dispatch(setToken(data.accessToken)); // Store the token in Redux } catch (error) { console.error('Login error:', error); } }, }), }), }); export const { useLoginMutation } = authApi;
-
postsApi (src/api/postsApi.js) :
-
Opérations CRUD : l'API des publications contient plusieurs points de terminaison pour interagir avec les publications (récupérer, créer, mettre à jour, supprimer).
- getPosts : récupère les publications paginées, ce qui signifie qu'il récupère les données en morceaux plus petits (pages), améliorant ainsi les performances et les temps de chargement.
- createPost, updatePost et deletePost : chacun d'entre eux effectue une action différente (créer, mettre à jour ou supprimer une publication).
- Balises pour la mise en cache : chaque point de terminaison utilise des balises (par exemple, { type : 'Posts', id }) pour gérer automatiquement l'invalidation et le rafraîchissement du cache. Par exemple, la création ou la suppression d'une publication invalide le cache, invitant getPosts à récupérer de nouvelles données sans intervention manuelle.
-
Opérations CRUD : l'API des publications contient plusieurs points de terminaison pour interagir avec les publications (récupérer, créer, mettre à jour, supprimer).
// src/store/store.js import AsyncStorage from '@react-native-async-storage/async-storage'; import { combineReducers, configureStore, isRejectedWithValue } from '@reduxjs/toolkit'; import { setupListeners } from '@reduxjs/toolkit/query'; import { FLUSH, PAUSE, PERSIST, persistReducer, PURGE, REGISTER, REHYDRATE } from 'redux-persist'; import { authApi } from '../api/authApi'; import { postsApi } from '../api/postsApi'; import { usersApi } from '../api/usersApi'; import authSlice from '../features/auth/authSlice'; const persistConfig = { key: 'root', version: 1, storage: AsyncStorage, blacklist: ['auth', postsApi.middleware, usersApi.middleware, authApi.middleware], // these reduce will not persist data (NOTE: blacklist rtk api slices so that to use tags) // whitelist: ['users'], //these reduce will persist data }; const getEnhancers = (getDefaultEnhancers) => { if (process.env.NODE_ENV === 'development') { const reactotron = require('../reactotronConfig/ReactotronConfig').default; return getDefaultEnhancers().concat(reactotron.createEnhancer()); } return getDefaultEnhancers(); }; /** * On api error this will be called */ export const rtkQueryErrorLogger = (api) => (next) => (action) => { // RTK Query uses `createAsyncThunk` from redux-toolkit under the hood, so we're able to utilize these matchers! if (isRejectedWithValue(action)) { console.log('isRejectedWithValue', action.error, action.payload); alert(JSON.stringify(action)); // This is just an example. You can replace it with your preferred method for displaying notifications. } return next(action); }; const reducer = combineReducers({ auth: authSlice, [postsApi.reducerPath]: postsApi.reducer, [usersApi.reducerPath]: usersApi.reducer, [authApi.reducerPath]: authApi.reducer, }); const persistedReducer = persistReducer(persistConfig, reducer); const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }).concat(postsApi.middleware, usersApi.middleware, authApi.middleware, rtkQueryErrorLogger), enhancers: getEnhancers, }); setupListeners(store.dispatch); export default store;
-
usersApi (src/api/usersApi.js) :
- Cette API récupère le profil de l'utilisateur authentifié, en configurant les en-têtes d'autorisation basés sur le jeton de Redux.
- En-têtes : PrepareHeaders attache dynamiquement le jeton à chaque demande s'il est disponible, permettant des requêtes API sécurisées et autorisées.
3. Auth Slice (src/features/auth/authSlice.js)
// src/api/authApi.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { setToken } from '../features/auth/authSlice'; export const authApi = createApi({ reducerPath: 'authApi', baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com/auth/', }), endpoints: (builder) => ({ login: builder.mutation({ query: (credentials) => ({ url: 'login', method: 'POST', body: credentials, }), async onQueryStarted(arg, { dispatch, queryFulfilled }) { try { const { data } = await queryFulfilled; dispatch(setToken(data.accessToken)); // Store the token in Redux } catch (error) { console.error('Login error:', error); } }, }), }), }); export const { useLoginMutation } = authApi;
- authSlice : Une tranche Redux gère un élément d'état spécifique, dans ce cas, l'authentification de l'utilisateur.
- Gestion de l'état : authSlice conserve le jeton de l'utilisateur, qui est utilisé pour accéder aux points de terminaison de l'API protégés.
-
Actions :
- setToken : stocke le jeton d'authentification dans l'état Redux.
- déconnexion : efface le jeton de Redux, déconnectant ainsi l'utilisateur.
4. Reactotron pour le débogage (src/reactotronConfig/ReactotronConfig.js)
// src/api/postsApi.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; // Define the postsApi slice with RTK Query export const postsApi = createApi({ // Unique key for the API slice in Redux state reducerPath: 'postsApi', // Configure base query settings, including the base URL for all requests baseQuery: fetchBaseQuery({ baseUrl: 'https://jsonplaceholder.typicode.com', }), // Define cache tag types for automatic cache invalidation tagTypes: ['Posts'], // Define API endpoints (queries and mutations) endpoints: (builder) => ({ // Query to fetch a paginated list of posts getPosts: builder.query({ // URL and parameters for paginated posts query: ({ page = 1, limit = 10 }) => `/posts?_page=${page}&_limit=${limit}`, // Tagging posts to automatically refresh this cache when needed providesTags: (result) => result ? [...result.map(({ id }) => ({ type: 'Posts', id })), { type: 'Posts', id: 'LIST' }] : [{ type: 'Posts', id: 'LIST' }], }), // Query to fetch a single post by its ID getPostById: builder.query({ // Define query with post ID in the URL path query: (id) => `/posts/${id}`, // Tag individual post by ID for selective cache invalidation providesTags: (result, error, id) => [{ type: 'Posts', id }], }), // Mutation to create a new post createPost: builder.mutation({ // Configure the POST request details and payload query: (newPost) => ({ url: '/posts', method: 'POST', body: newPost, }), // Invalidate all posts (paginated list) to refresh after creating a post invalidatesTags: [{ type: 'Posts', id: 'LIST' }], }), // Mutation to update an existing post by its ID updatePost: builder.mutation({ // Define the PUT request with post ID and updated data in the payload query: ({ id, ...updatedData }) => ({ url: `/posts/${id}`, method: 'PUT', body: updatedData, }), // Invalidate cache for both the updated post and the paginated list invalidatesTags: (result, error, { id }) => [ { type: 'Posts', id }, { type: 'Posts', id: 'LIST' }, ], }), // Mutation to delete a post by its ID deletePost: builder.mutation({ // Define the DELETE request with post ID in the URL path query: (id) => ({ url: `/posts/${id}`, method: 'DELETE', }), // Invalidate cache for the deleted post and the paginated list invalidatesTags: (result, error, id) => [ { type: 'Posts', id }, { type: 'Posts', id: 'LIST' }, ], }), }), }); // Export generated hooks for each endpoint to use them in components export const { useGetPostsQuery, // Use this when you want data to be fetched automatically as the component mounts or when the query parameters change. useLazyGetPostsQuery, // Use this when you need more control over when the query runs, such as in response to a user action (e.g., clicking a button), conditional fetching, or specific events. useGetPostByIdQuery, useCreatePostMutation, useUpdatePostMutation, useDeletePostMutation, } = postsApi;
- Reactotron : Reactotron est un outil de débogage qui permet de suivre les changements d'état de Redux, de surveiller les demandes d'API et d'inspecter les journaux.
- Configuration : configuré pour capturer les sorties console.log et les actions Redux. En mode développement, cette configuration fournit un moyen puissant de déboguer sans ajouter de code supplémentaire ni altérer les performances de production.
5. Principaux composants de l'application
// src/api/usersApi.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; export const usersApi = createApi({ reducerPath: 'usersApi', baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com', prepareHeaders: (headers, { getState }) => { // Get the token from the Redux auth state const { token } = getState().auth; // If the token exists, set it in the Authorization header if (token) { headers.set('Authorization', `Bearer ${token}`); } // Optional: include credentials if needed by the API headers.set('credentials', 'include'); return headers; }, }), endpoints: (builder) => ({ // Fetch user profile with token in Authorization header getUserProfile: builder.query({ query: () => '/auth/me', }), }), }); export const { useGetUserProfileQuery } = usersApi;
-
Composant d'application (src/App.js) :
- Le composant App enveloppe l'intégralité de l'application dans Provider (pour rendre Redux disponible) et PersistGate (pour retarder le rendu jusqu'à ce que l'état persistant ait été récupéré).
- PersistGate garantit le chargement des données persistantes avant l'affichage de l'application, réduisant ainsi les incohérences du temps de chargement.
// src/MainApp.js importer React, { useEffect, useState } depuis 'react' ; importer { Indicateur d'activité, Bouton, Liste plate, Modal, ActualiserContrôle, Feuille de style, Texte, Entrée de texte, Voir, } de « réagir-natif » ; importer { SafeAreaView } depuis 'react-native-safe-area-context' ; importer { useDispatch, useSelector } depuis 'react-redux' ; importer { useLoginMutation } depuis './api/authApi' ; importer { utilisezCreatePostMutation, useDeletePostMutation, utilisezGetPostsQuery, utilisezLazyGetPostsQuery, useUpdatePostMutation, } depuis './api/postsApi'; importer { useGetUserProfileQuery } depuis './api/usersApi' ; importer { déconnexion }de './features/auth/authSlice'; const MainApp = () => { const [newPostTitle, setNewPostTitle] = useState(''); const [page, setPage] = useState(1); const [postsData, setPostsData] = useState([]); const [rafraîchissant, setRefreshing] = useState(false); const [isModalVisible, setModalVisible] = useState(false); const dispatch = useDispatch(); const token = useSelector((state) => state.auth.token); // Mutation de connexion const [login, { isLoading : isLoggingIn }] = useLoginMutation(); // Récupère le profil utilisateur lorsque le jeton est disponible const { données : userProfile, refetch : refetchUserProfile } = useGetUserProfileQuery (non défini, { sauter : !jeton, }); // Récupère les messages paginés const { données : postes, est en cours de chargement, estRécupération, estErreur, récupérer, } = useGetPostsQuery({ page, limite : 10 }); // Le hook useQuery est utilisé lorsque vous souhaitez récupérer des données lors du chargement de l'écran. Par exemple, récupérez le profil utilisateur sur l'écran de profil. // Utilisez la requête paresseuse pour actualiser pour récupérer directement la page 1 const [triggerFetchFirstPage, { data: lazyData }] = useLazyGetPostsQuery(); // useLazyquery est utilisé lorsque vous souhaitez contrôler l'appel de l'API, comme lors d'un clic sur un bouton. const [createPost] = useCreatePostMutation(); const [updatePost] = useUpdatePostMutation(); const [deletePost] = useDeletePostMutation(); useEffect(() => { si (messages) { setPostsData((prevData) => (page === 1 ? posts : [...prevData, ...posts])); } }, [messages, page]); // Gestionnaire de connexion const handleLogin = async () => { essayer { const identifiants = { nom d'utilisateur : 'emilys', mot de passe : 'emilyspass' } ; attendre la connexion (informations d'identification) ; console.log('userProfile', userProfile); refetchUserProfile(); } attraper (erreur) { console.error('Échec de la connexion :', erreur); } } ; const handleRefresh = async () => { setRefreshing(true); setPage(1); // Remet la page à 1 pour les prochains scrolls setPostsData([]); // Efface les données pour éviter les duplications // Déclenche explicitement la récupération de la première page const { data } = wait triggerFetchFirstPage({ page : 1, limite : 10 }); si (données) { setPostsData(données); // Définit les données des publications sur les résultats de la première page } setRefreshing(faux); } ; // Crée un nouveau message, l'ajoute en haut et récupère la liste const handleCreatePost = async () => { si (nouveau titre de poste) { const { data : newPost } = wait createPost ({ title : newPostTitle, body : 'Nouveau contenu du message' }); setNewPostTitle(''); setPostsData((prevData) => [newPost, ...prevData]); récupérer(); } } ; // Mettre à jour un article existant et ajouter "HASAN" à son titre const handleUpdatePost = async (post) => { const { data : updatePost } = wait updatePost ({ identifiant : post.id, titre : `${post.title} HASAN`, }); setPostsData((prevData) => prevData.map((item) => (item?.id === updatePost?.id ? updatePost : item)) ); } ; // Supprime une publication et la supprime immédiatement de l'interface utilisateur const handleDeletePost = async (id) => { attendre deletePost(id); setPostsData((prevData) => prevData.filter((post) => post.id !== id)); } ; // Charge plus de messages pour un défilement infini const loadMorePosts = () => { si (!isFetching) { setPage((prevPage) => prevPage 1); } } ; // Basculer la visibilité modale const toggleModal = () => { setModalVisible(!isModalVisible); } ; if (isLoading && page === 1) return <text>Loading...</text>; if (isError) return <text>Erreur lors de la récupération des publications.</text>; retour ( <safeareaview> <ul> <li> <strong>Composant MainApp (src/MainApp.js)</strong> : <ul> <li> <strong>State and Hooks</strong> : gère les états locaux (par exemple, pour la pagination des publications) et les hooks comme useLoginMutation pour déclencher des actions sur des événements spécifiques.</li> <li> <strong>Connexion</strong> : <ul> <li>Utilise useLoginMutation pour connecter l'utilisateur, puis déclenche refetchUserProfile pour charger les données du profil utilisateur.</li> <li> <em>Requête conditionnelle</em> : récupère le profil utilisateur uniquement lorsqu'un jeton valide existe (ignorer : !token), réduisant ainsi les appels d'API inutiles.</li> </ul> </li> <li> <strong>Récupération des messages</strong> : <ul> <li>Utilise useGetPostsQuery pour récupérer les publications paginées, prenant en charge le défilement infini en récupérant plus de données à mesure que l'utilisateur fait défiler.</li> <li> <em>Contrôle d'actualisation</em> : permet aux utilisateurs d'actualiser la liste des publications, utile pour la fonctionnalité d'extraction pour actualiser sur mobile.</li> </ul> </li> <li> <strong>Créer, mettre à jour, supprimer des publications</strong> : <ul> <li> <em>Create</em> : appelle createPost, mettant immédiatement à jour la liste des publications avec la nouvelle publication en haut.</li> <li> <em>Mise à jour</em> : ajoute « HASAN » au titre d'un article lors de la mise à jour.</li> <li> <em>Supprimer</em> : supprime une publication et met à jour l'interface utilisateur sans avoir besoin de recharger la page, grâce à l'invalidation du cache de deletePost.</li> </ul> </li> <li> <strong>Éléments de l'interface utilisateur</strong> : <ul> <li>Un modal affiche le profil utilisateur. Le bouton de profil n'apparaît que si les données userProfile sont chargées, améliorant ainsi l'expérience utilisateur.</li> </ul> </li> <li> <strong>FlatList</strong> : affiche les publications dans un format défilant et paginé, améliorant ainsi la convivialité.</li> </ul> </li> </ul> <hr> <h2> Résumé: </h2> <p>Votre application React Native utilise <strong>Redux Toolkit (RTK) Query</strong> pour une gestion efficace des données et des interactions API. La configuration comprend :</p> <ol> <li><p><strong>Configuration du magasin</strong> : magasin Redux avec redux-persist pour enregistrer des données spécifiques sur les sessions d'application, un middleware personnalisé pour la journalisation des erreurs et Reactotron pour le débogage en mode développement.</p></li> <li> <p><strong>API avec requête RTK</strong> :</p> <ul> <li> <strong>authApi</strong> gère l'authentification avec une mutation de connexion, stockant le jeton dans Redux.</li> <li> <strong>postsApi</strong> fournit des opérations CRUD pour les publications, en utilisant des balises de cache pour actualiser automatiquement les données lorsque les publications sont ajoutées, mises à jour ou supprimées.</li> <li> <strong>usersApi</strong> récupère le profil utilisateur avec des en-têtes d'autorisation dynamiques basés sur des jetons.</li> </ul> </li> <li><p><strong>Auth Slice</strong> : gère le jeton d'authentification et fournit des actions pour définir ou effacer le jeton lors de la connexion/déconnexion.</p></li> <li> <p><strong>Composants App et MainApp</strong> :</p> <ul> <li>L'application principale enveloppe les composants dans Provider et PersistGate, garantissant que l'état est chargé avant le rendu.</li> <li> MainApp gère la récupération, la création, la mise à jour et la suppression des publications. Il charge les données de manière conditionnelle (par exemple, récupère le profil utilisateur uniquement lorsqu'un jeton existe), prend en charge la pagination et le défilement infini </li> <li>Utilise FlatList pour une liste de publications paginée, des modaux pour le profil et des styles de base pour une mise en page propre et organisée.</li> </ul> </li> </ol> <blockquote> <p>CODE COMPLET-></p> </blockquote> </safeareaview>
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!

Les principales utilisations de JavaScript dans le développement Web incluent l'interaction client, la vérification du formulaire et la communication asynchrone. 1) Mise à jour du contenu dynamique et interaction utilisateur via les opérations DOM; 2) La vérification du client est effectuée avant que l'utilisateur ne soumette les données pour améliorer l'expérience utilisateur; 3) La communication de rafraîchissement avec le serveur est réalisée via la technologie AJAX.

Comprendre le fonctionnement du moteur JavaScript en interne est important pour les développeurs car il aide à écrire du code plus efficace et à comprendre les goulots d'étranglement des performances et les stratégies d'optimisation. 1) Le flux de travail du moteur comprend trois étapes: analyse, compilation et exécution; 2) Pendant le processus d'exécution, le moteur effectuera une optimisation dynamique, comme le cache en ligne et les classes cachées; 3) Les meilleures pratiques comprennent l'évitement des variables globales, l'optimisation des boucles, l'utilisation de const et de locations et d'éviter une utilisation excessive des fermetures.

Python convient plus aux débutants, avec une courbe d'apprentissage en douceur et une syntaxe concise; JavaScript convient au développement frontal, avec une courbe d'apprentissage abrupte et une syntaxe flexible. 1. La syntaxe Python est intuitive et adaptée à la science des données et au développement back-end. 2. JavaScript est flexible et largement utilisé dans la programmation frontale et côté serveur.

Python et JavaScript ont leurs propres avantages et inconvénients en termes de communauté, de bibliothèques et de ressources. 1) La communauté Python est amicale et adaptée aux débutants, mais les ressources de développement frontal ne sont pas aussi riches que JavaScript. 2) Python est puissant dans les bibliothèques de science des données et d'apprentissage automatique, tandis que JavaScript est meilleur dans les bibliothèques et les cadres de développement frontaux. 3) Les deux ont des ressources d'apprentissage riches, mais Python convient pour commencer par des documents officiels, tandis que JavaScript est meilleur avec MDNWEBDOCS. Le choix doit être basé sur les besoins du projet et les intérêts personnels.

Le passage de C / C à JavaScript nécessite de s'adapter à la frappe dynamique, à la collecte des ordures et à la programmation asynchrone. 1) C / C est un langage dactylographié statiquement qui nécessite une gestion manuelle de la mémoire, tandis que JavaScript est dynamiquement typé et que la collecte des déchets est automatiquement traitée. 2) C / C doit être compilé en code machine, tandis que JavaScript est une langue interprétée. 3) JavaScript introduit des concepts tels que les fermetures, les chaînes de prototypes et la promesse, ce qui améliore la flexibilité et les capacités de programmation asynchrones.

Différents moteurs JavaScript ont des effets différents lors de l'analyse et de l'exécution du code JavaScript, car les principes d'implémentation et les stratégies d'optimisation de chaque moteur diffèrent. 1. Analyse lexicale: convertir le code source en unité lexicale. 2. Analyse de la grammaire: générer un arbre de syntaxe abstrait. 3. Optimisation et compilation: générer du code machine via le compilateur JIT. 4. Exécuter: Exécutez le code machine. Le moteur V8 optimise grâce à une compilation instantanée et à une classe cachée, SpiderMonkey utilise un système d'inférence de type, résultant en différentes performances de performances sur le même code.

Les applications de JavaScript dans le monde réel incluent la programmation côté serveur, le développement des applications mobiles et le contrôle de l'Internet des objets: 1. La programmation côté serveur est réalisée via Node.js, adaptée au traitement de demande élevé simultané. 2. Le développement d'applications mobiles est effectué par le reactnatif et prend en charge le déploiement multiplateforme. 3. Utilisé pour le contrôle des périphériques IoT via la bibliothèque Johnny-Five, adapté à l'interaction matérielle.

J'ai construit une application SAAS multi-locataire fonctionnelle (une application EdTech) avec votre outil technologique quotidien et vous pouvez faire de même. Premièrement, qu'est-ce qu'une application SaaS multi-locataire? Les applications saas multi-locataires vous permettent de servir plusieurs clients à partir d'un chant


Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Version Mac de WebStorm
Outils de développement JavaScript utiles

Dreamweaver CS6
Outils de développement Web visuel

Télécharger la version Mac de l'éditeur Atom
L'éditeur open source le plus populaire

DVWA
Damn Vulnerable Web App (DVWA) est une application Web PHP/MySQL très vulnérable. Ses principaux objectifs sont d'aider les professionnels de la sécurité à tester leurs compétences et leurs outils dans un environnement juridique, d'aider les développeurs Web à mieux comprendre le processus de sécurisation des applications Web et d'aider les enseignants/étudiants à enseigner/apprendre dans un environnement de classe. Application Web sécurité. L'objectif de DVWA est de mettre en pratique certaines des vulnérabilités Web les plus courantes via une interface simple et directe, avec différents degrés de difficulté. Veuillez noter que ce logiciel

Navigateur d'examen sécurisé
Safe Exam Browser est un environnement de navigation sécurisé permettant de passer des examens en ligne en toute sécurité. Ce logiciel transforme n'importe quel ordinateur en poste de travail sécurisé. Il contrôle l'accès à n'importe quel utilitaire et empêche les étudiants d'utiliser des ressources non autorisées.