이 가이드에서 다룰 내용은 다음과 같습니다.
- CRUD 작업
- 페이지 매김
- RTK 쿼리를 사용한 Redux Persist
- 다중 기본 URL 사용
- 보호된 공공 경로
- 캐시 관리 및 무효화
RTK 쿼리는 Redux Toolkit(RTK)에 내장된 고급 데이터 가져오기 및 캐싱 도구입니다. 데이터 가져오기, 캐싱, 업데이트와 같은 일반적인 작업을 위한 Redux 슬라이스 및 후크를 생성하여 API 상호 작용을 간소화합니다. 주요 기능은 다음과 같습니다:
- 자동 캐싱: RTK 쿼리는 데이터를 캐시하고 데이터가 무효화되면 자동으로 다시 가져오므로 UI에 항상 최신 데이터가 유지됩니다.
- 캐시 무효화: 태그를 사용하면 RTK 쿼리를 통해 특정 데이터를 다시 가져와야 하는 시기를 정의할 수 있습니다. 이렇게 하면 데이터를 수동으로 업데이트하지 않고도 캐시를 최신 상태로 유지할 수 있습니다.
- 자동 생성 후크: RTK 쿼리는 각 API 엔드포인트에 대한 후크를 생성하므로 간단한 React 후크(useGetPostsQuery, useCreatePostMutation 등)를 사용하여 API를 호출할 수 있습니다.
- 오류 처리: 미들웨어를 통한 맞춤형 오류 처리가 포함되어 있어 오류를 쉽게 포착하고 표시할 수 있습니다.
- 간소화된 Redux 통합: RTK 쿼리는 Redux와 직접 통합되므로 전역 상태 관리 또는 캐싱을 위한 추가 라이브러리가 필요하지 않습니다.
RTK 쿼리와 React 쿼리
React 쿼리와 RTK 쿼리는 모두 React 애플리케이션에서 데이터 가져오기 및 캐싱을 위한 솔루션을 제공하지만 장점과 사용 사례가 다릅니다.
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. |
RTK 쿼리와 React 쿼리 중에서 선택:
-
RTK 쿼리 사용 다음과 같은 경우:
- 이미 Redux를 사용하고 있으며 데이터 가져오기를 위한 통합되고 간소화된 솔루션을 원합니다.
- Redux 내에서 중앙 집중식 오류 처리 및 개발 도구 통합이 필요합니다.
-
React 쿼리 사용 다음과 같은 경우:
- Redux 종속성이 없는 보다 가벼운 설정을 원합니다.
- 별도의 서버 상태 관리를 선호하며 전역 앱 상태가 필요하지 않습니다.
기본적으로 RTK 쿼리는 Redux 중심 애플리케이션에 탁월한 반면, React 쿼리는 Redux가 없는 프로젝트나 보다 지역화된 서버 상태 관리에 초점을 맞춘 프로젝트에 유연성과 단순성을 제공합니다.
1. 스토어 구성 및 설정
// 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): Redux 저장소는 애플리케이션의 상태를 유지하는 주요 구조입니다. 설정에서 Redux 상태의 특정 부분을 로컬에 저장하는 redux-persist가 향상되어 앱을 다시 시작해도 지속됩니다.
-
redux-지속성:
- 목적: Redux 상태의 일부를 앱 세션 전반에 걸쳐 지속적으로 유지하는 데 도움이 됩니다.
- 구성: persistConfig 개체는 auth, postApi 및 usersApi가 지속되지 않도록(블랙리스트에 추가) 지정합니다. 즉, 앱을 다시 시작할 때 해당 데이터가 재설정됩니다.
- persistReducer는 감속기 구성과 지속성 기능을 결합합니다.
Enhancer: 사용자 정의 Enhancer는 Redux 작업, 상태 및 네트워크 요청을 디버깅하는 데 유용한 도구인 Reactotron을 개발 모드에 통합하는 데 사용됩니다. 이는 개발 중에만 활성화되므로 프로덕션에 영향을 주지 않고 디버깅이 더 쉬워집니다.
-
미들웨어:
- RTK 쿼리 미들웨어(postsApi.middleware, usersApi.middleware, authApi.middleware)는 자동 캐시 관리 기능을 추가하여 데이터 가져오기를 효율적으로 만듭니다.
- rtkQueryErrorLogger: API 호출이 실패할 때 사용자 정의 미들웨어가 오류를 기록합니다. RTK 쿼리의 isRejectedWithValue 함수를 사용하여 오류를 포착하고 처리하므로 사용자에게 문제에 대해 알리거나 다른 조치를 취할 수 있습니다.
setupListeners: 이 기능을 사용하면 앱이 포커스를 다시 얻거나 백그라운드에서 다시 시작할 때와 같이 특정 이벤트가 발생할 때 데이터를 자동으로 다시 가져올 수 있어 수동으로 새로 고칠 필요 없이 사용자에게 새로운 데이터를 제공할 수 있습니다.
2. RTK 쿼리를 사용한 API 정의
RTK 쿼리는 Redux 슬라이스, 후크 및 캐싱을 자동 생성하여 API 호출을 단순화합니다. 귀하가 정의한 API에 대한 분석은 다음과 같습니다.
// 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):
- 인증을 위해 사용자 자격 증명(예: 사용자 이름, 비밀번호)을 서버로 보내는 로그인 변형을 정의합니다.
- onQueryStarted: 로그인이 성공하면 setToken 작업을 사용하여 반환된 토큰을 Redux에 저장합니다. 이를 통해 다른 엔드포인트에 대한 안전하고 인증된 요청이 가능해집니다.
// 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):
-
CRUD 작업: 게시물 API에는 게시물과 상호작용(가져오기, 생성, 업데이트, 삭제)하는 여러 엔드포인트가 포함되어 있습니다.
- getPosts: 페이지가 매겨진 게시물을 가져옵니다. 즉, 더 작은 청크(페이지)로 데이터를 검색하여 성능과 로드 시간을 향상시킵니다.
- createPost, updatePost 및 deletePost: 각각 다른 작업(게시물 생성, 업데이트 또는 삭제)을 수행합니다.
- 캐싱용 태그: 각 엔드포인트는 태그를 사용합니다(예: { type: 'Posts', id }) 캐시 무효화 및 새로 고침을 자동으로 관리합니다. 예를 들어, 게시물을 생성하거나 삭제하면 캐시가 무효화되어 수동 개입 없이 getPosts가 새로운 데이터를 가져오라는 메시지가 표시됩니다.
-
CRUD 작업: 게시물 API에는 게시물과 상호작용(가져오기, 생성, 업데이트, 삭제)하는 여러 엔드포인트가 포함되어 있습니다.
// 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):
- 이 API는 인증된 사용자의 프로필을 가져와 Redux의 토큰을 기반으로 Authorization 헤더를 설정합니다.
- 헤더: prepareHeaders는 가능한 경우 모든 요청에 토큰을 동적으로 첨부하여 안전하고 승인된 API 요청을 허용합니다.
3. 인증 슬라이스(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: Redux 슬라이스는 특정 상태(이 경우 사용자 인증)를 관리합니다.
- 상태 관리: authSlice는 보호된 API 엔드포인트에 액세스하는 데 사용되는 사용자 토큰을 유지합니다.
-
작업:
- setToken: 인증 토큰을 Redux 상태로 저장합니다.
- logout: Redux에서 토큰을 지워 사용자를 효과적으로 로그아웃시킵니다.
4. 디버깅을 위한 Reactotron(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은 Redux 상태 변경을 추적하고, API 요청을 모니터링하고, 로그를 검사하는 데 도움이 되는 디버깅 도구입니다.
- 설정: console.log 출력 및 Redux 작업을 캡처하도록 구성됩니다. 개발 모드에서 이 설정은 추가 코드를 추가하거나 프로덕션 성능을 변경하지 않고 디버깅할 수 있는 강력한 방법을 제공합니다.
5. 주요 애플리케이션 구성요소
// 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;
-
앱 구성 요소(src/App.js):
- App 구성 요소는 전체 애플리케이션을 Provider(Redux를 사용 가능하게 만들기 위해) 및 PersistGate(지속 상태가 검색될 때까지 렌더링을 지연하기 위해)로 래핑합니다.
- PersistGate는 앱이 표시되기 전에 지속적인 데이터 로드를 보장하여 로드 시간 불일치를 줄입니다.
// src/MainApp.js import React, { useEffect, useState } from 'react'; 수입 { 활동표시기, 단추, 플랫리스트, 모달, 새로고침컨트롤, 스타일시트, 텍스트, 텍스트 입력, 보다, } '반응 네이티브'에서; 'react-native-safe-area-context'에서 { SafeAreaView }를 가져옵니다. import { useDispatch, useSelector } from 'react-redux'; import { useLoginMutation } from './api/authApi'; 수입 { useCreatePostMutation, useDeletePostMutation, useGetPostsQuery, useLazyGetPostsQuery, useUpdatePostMutation, } './api/postsApi'에서; import { useGetUserProfileQuery } from './api/usersApi'; 가져오기 { 로그아웃 }'./features/auth/authSlice'에서; const MainApp = () => { const [newPostTitle, setNewPostTitle] = useState(''); const [페이지, setPage] = useState(1); const [postsData, setPostsData] = useState([]); const [새로고침, setRefreshing] = useState(false); const [isModalVisible, setModalVisible] = useState(false); const 디스패치 = useDispatch(); const 토큰 = useSelector((state) => state.auth.token); // 로그인 돌연변이 const [login, { isLoading: isLoggingIn }] = useLoginMutation(); // 토큰을 사용할 수 있을 때 사용자 프로필을 가져옵니다. const { 데이터: userProfile, 다시 가져오기: refetchUserProfile } = useGetUserProfileQuery(정의되지 않음, { 건너뛰기: !토큰, }); // 페이지가 매겨진 게시물을 가져옵니다. const { 데이터: 게시물, 로드 중, 가져오는 중, isError, 다시 가져오기, } = useGetPostsQuery({ 페이지, 제한: 10 }); // useQuery 후크는 화면 로드 시 데이터를 가져오려는 경우에 사용됩니다. 예를 들어 프로필 화면에서 사용자 프로필을 가져옵니다. // 페이지 1을 직접 가져오기 위해 새로고침 지연 쿼리를 사용합니다. const [triggerFetchFirstPage, { data:lazyData }] = useLazyGetPostsQuery(); // useLazyquery는 버튼 클릭과 같이 API 호출을 제어하려는 경우에 사용됩니다. const [createPost] = useCreatePostMutation(); const [updatePost] = useUpdatePostMutation(); const [deletePost] = useDeletePostMutation(); useEffect(() => { if (게시물) { setPostsData((prevData) => (페이지 === 1 ? 게시물: [...prevData, ...게시물])); } }, [게시물, 페이지]); // 로그인 핸들러 const handlerLogin = async () => { 노력하다 { const 자격 증명 = { 사용자 이름: 'emilys', 비밀번호: 'emilyspass' }; 로그인을 기다립니다(자격 증명); console.log('userProfile', userProfile); refetchUserProfile(); } 잡기(오류) { console.error('로그인 실패:', error); } }; const handlerRefresh = async () => { setRefreshing(true); setPage(1); // 다음 스크롤을 위해 페이지를 1로 재설정합니다. setPostsData([]); // 중복을 피하기 위해 데이터를 지웁니다. // 첫 번째 페이지 가져오기를 명시적으로 트리거합니다. const { data } = TriggerFetchFirstPage({ 페이지: 1, 제한: 10 })를 기다립니다. if (데이터) { setPostsData(데이터); // 게시물 데이터를 첫 번째 페이지의 결과로 설정합니다. } setRefreshing(false); }; // 새 게시물을 작성하여 상단에 추가하고 목록을 다시 가져옵니다. const handlerCreatePost = async () => { if (newPostTitle) { const { data: newPost } = createPost({ title: newPostTitle, body: '새 게시물 콘텐츠' })를 기다리고 있습니다. setNewPostTitle(''); setPostsData((prevData) => [newPost, ...prevData]); 다시 가져오기(); } }; // 기존 게시물을 업데이트하고 제목에 "HASAN"을 추가합니다. const handlerUpdatePost = 비동기(포스트) => { const { data:updatePost } = updatePost를 기다립니다({ id: post.id, 제목: `${post.title} 하산`, }); setPostsData((prevData) => prevData.map((item) => (item?.id ===updatePost?.id ?updatePost : item)) ); }; // 게시물을 삭제하고 UI에서 즉시 제거합니다. const handlerDeletePost = async (id) => { deletePost(id)를 기다립니다; setPostsData((prevData) => prevData.filter((post) => post.id !== id)); }; // 무한 스크롤을 위해 더 많은 게시물 로드 const loadMorePosts = () => { if (!isFetching) { setPage((prevPage) => prevPage 1); } }; // 모달 가시성 전환 const 토글모달 = () => { setModalVisible(!isModalVisible); }; if (isLoading && page === 1) return <text>Loading...</text> if (isError) return <text>게시물 가져오기 오류.</text> 반품 ( <safeareaview> <ul> <li> <strong>MainApp 구성 요소(src/MainApp.js)</strong>: <ul> <li> <strong>상태 및 후크</strong>: 로컬 상태(예: 게시물 페이지 매김) 및 useLoginMutation과 같은 후크를 관리하여 특정 이벤트에 대한 작업을 트리거합니다.</li> <li> <strong>로그인</strong>: <ul> <li>useLoginMutation을 사용하여 사용자를 로그인한 다음 refetchUserProfile을 트리거하여 사용자 프로필 데이터를 로드합니다.</li> <li> <em>조건부 쿼리</em>: 유효한 토큰이 존재할 때만 사용자 프로필을 가져오므로(건너뛰기: !token) 불필요한 API 호출을 줄입니다.</li> </ul> </li> <li> <strong>게시물 가져오는 중</strong>: <ul> <li>useGetPostsQuery를 사용하여 페이지가 매겨진 게시물을 가져오고 사용자가 스크롤할 때 더 많은 데이터를 가져와 무한 스크롤을 지원합니다.</li> <li> <em>새로고침 제어</em>: 사용자가 게시물 목록을 새로 고칠 수 있으며, 모바일에서 당겨서 새로 고침 기능에 유용합니다.</li> </ul> </li> <li> <strong>게시물 생성, 업데이트, 삭제</strong>: <ul> <li> <em>Create</em>: createPost를 호출하여 게시물 목록을 상단에 새 게시물로 즉시 업데이트합니다.</li> <li> <em>업데이트</em>: 업데이트 시 게시물 제목에 "HASAN"을 추가합니다.</li> <li> <em>삭제</em>: deletePost의 캐시 무효화 덕분에 페이지를 다시 로드할 필요 없이 게시물을 제거하고 UI를 업데이트합니다.</li> </ul> </li> <li> <strong>UI 요소</strong>: <ul> <li>모달은 사용자 프로필을 표시합니다. 프로필 버튼은 userProfile 데이터가 로드된 경우에만 나타나므로 사용자 경험이 향상됩니다.</li> </ul> </li> <li> <strong>FlatList</strong>: 게시물을 스크롤 가능하고 페이지가 매겨진 형식으로 표시하여 유용성을 높입니다.</li> </ul> </li> </ul> <hr> <h2> 요약: </h2> <p>React Native 앱은 효율적인 데이터 관리 및 API 상호 작용을 위해 <strong>Redux Toolkit(RTK) 쿼리</strong>를 사용합니다. 설정에는 다음이 포함됩니다.</p> <ol> <li><p><strong>스토어 구성</strong>: 앱 세션 전체에서 특정 데이터를 저장하기 위한 redux-persist, 오류 로깅을 위한 맞춤형 미들웨어, 개발 모드에서 디버깅을 위한 Reactotron을 갖춘 Redux 스토어.</p></li> <li> <p><strong>RTK 쿼리가 포함된 API</strong>:</p> <ul> <li> <strong>authApi</strong>는 로그인 변형으로 인증을 처리하고 토큰을 Redux에 저장합니다.</li> <li> <strong>postsApi</strong>는 게시물이 추가, 업데이트 또는 삭제될 때 캐시 태그를 사용하여 자동으로 데이터를 새로 고치는 게시물에 대한 CRUD 작업을 제공합니다.</li> <li> <strong>usersApi</strong>는 동적 토큰 기반 인증 헤더를 사용하여 사용자 프로필을 가져옵니다.</li> </ul> </li> <li><p><strong>Auth Slice</strong>: 인증 토큰을 관리하고 로그인/로그아웃 시 토큰을 설정하거나 삭제하는 작업을 제공합니다.</p></li> <li> <p><strong>앱 및 MainApp 구성요소</strong>:</p> <ul> <li>메인 앱은 Provider 및 PersistGate의 구성 요소를 래핑하여 렌더링 전에 상태가 로드되도록 합니다.</li> <li> MainApp은 게시물 가져오기, 생성, 업데이트 및 삭제를 관리합니다. 조건부로 데이터를 로드하고(예: 토큰이 존재할 때만 사용자 프로필 가져오기) 페이지 매김 및 무한 스크롤을 지원합니다. </li> <li>페이지가 매겨진 게시물 목록에는 FlatList를 사용하고, 프로필에는 모달을 사용하고, 깔끔하고 정리된 레이아웃을 위한 기본 스타일을 사용합니다.</li> </ul> </li> </ol> <blockquote> <p>전체 코드-></p> </blockquote> </safeareaview>
위 내용은 RTK 쿼리를 사용한 React Native의 효율적인 데이터 처리의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

JavaScript 문자열 교체 방법 및 FAQ에 대한 자세한 설명 이 기사는 JavaScript에서 문자열 문자를 대체하는 두 가지 방법 인 내부 JavaScript 코드와 웹 페이지의 내부 HTML을 탐색합니다. JavaScript 코드 내부의 문자열을 교체하십시오 가장 직접적인 방법은 대체 () 메소드를 사용하는 것입니다. str = str.replace ( "find", "replace"); 이 메소드는 첫 번째 일치 만 대체합니다. 모든 경기를 교체하려면 정규 표현식을 사용하고 전역 플래그 g를 추가하십시오. str = str.replace (/fi

간단한 자바 스크립트 함수는 날짜가 유효한지 확인하는 데 사용됩니다. 기능 isValidDate (s) { var 비트 = s.split ( '/'); var d = 새로운 날짜 (비트 [2]/'비트 [1]/'비트 [0]); return !! (d && (d.getmonth () 1) == 비트 [1] && d.getDate () == 숫자 (비트 [0]); } //시험 var

이 기사에서는 jQuery를 사용하여 DOM 요소의 내부 마진 및 마진 값, 특히 요소의 외부 마진 및 내부 마진의 특정 위치를 얻고 설정하는 방법에 대해 설명합니다. CSS를 사용하여 요소의 내부 및 외부 마진을 설정할 수는 있지만 정확한 값을 얻는 것이 까다로울 수 있습니다. // 설정 $ ( "div.header"). css ( "margin", "10px"); $ ( "Div.Header"). CSS ( "패딩", "10px"); 이 코드는 생각할 수 있습니다

이 기사는 10 개의 탁월한 jQuery 탭과 아코디언을 탐구합니다. 탭과 아코디언의 주요 차이점은 콘텐츠 패널이 표시되고 숨겨진 방식에 있습니다. 이 10 가지 예를 살펴 보겠습니다. 관련 기사 : 10 JQuery Tab 플러그인

웹 사이트의 역학 및 시각적 매력을 높이기 위해 10 개의 탁월한 jQuery 플러그인을 발견하십시오! 이 선별 된 컬렉션은 이미지 애니메이션에서 대화식 갤러리에 이르기까지 다양한 기능을 제공합니다. 이 강력한 도구를 탐색합시다. 관련 게시물 : 1

HTTP-Console은 HTTP 명령을 실행하기위한 명령 줄 인터페이스를 제공하는 노드 모듈입니다. 웹 서버, 웹 서비스에 대해 만들어 졌는지 여부에 관계없이 HTTP 요청과 함께 어떻게 진행되고 있는지 정확하게 보는 데 유용합니다.

이 튜토리얼은 사용자 정의 Google 검색 API를 블로그 또는 웹 사이트에 통합하는 방법을 보여 주며 표준 WordPress 테마 검색 기능보다보다 세련된 검색 경험을 제공합니다. 놀랍게도 쉽습니다! 검색을 Y로 제한 할 수 있습니다

다음 jQuery 코드 스 니펫은 DIV 내용이 컨테이너 요소 영역을 초과 할 때 스크롤 바를 추가하는 데 사용될 수 있습니다. (데모 없음, FireBug에 직접 복사하십시오) // d = 문서 // w = 창 // $ = jQuery var contentArea = $ (this), wintop = contentArea.scrolltop (), docheight = $ (d) .height (), winheight = $ (w) .height (), divheight = $ ( '#c


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

DVWA
DVWA(Damn Vulnerable Web App)는 매우 취약한 PHP/MySQL 웹 애플리케이션입니다. 주요 목표는 보안 전문가가 법적 환경에서 자신의 기술과 도구를 테스트하고, 웹 개발자가 웹 응용 프로그램 보안 프로세스를 더 잘 이해할 수 있도록 돕고, 교사/학생이 교실 환경 웹 응용 프로그램에서 가르치고 배울 수 있도록 돕는 것입니다. 보안. DVWA의 목표는 다양한 난이도의 간단하고 간단한 인터페이스를 통해 가장 일반적인 웹 취약점 중 일부를 연습하는 것입니다. 이 소프트웨어는

Atom Editor Mac 버전 다운로드
가장 인기 있는 오픈 소스 편집기

Dreamweaver Mac版
시각적 웹 개발 도구

PhpStorm 맥 버전
최신(2018.2.1) 전문 PHP 통합 개발 도구

SecList
SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.
