このガイドでは以下について説明します:
- CRUD オペレーション
- ページネーション
- RTK クエリを使用した Redux 永続化
- 複数のベース URL の使用
- 保護された公共ルート
- キャッシュの管理と無効化
RTK Query は、Redux Toolkit (RTK) に組み込まれた高度なデータフェッチおよびキャッシュ ツールです。データのフェッチ、キャッシュ、更新などの一般的なタスク用の Redux スライスとフックを生成することで、API の対話を合理化します。主な機能は次のとおりです:
- 自動キャッシュ: RTK クエリはデータをキャッシュし、データが無効になったときに自動的に再取得して、UI に常に最新のデータが含まれるようにします。
- キャッシュの無効化: タグを使用すると、RTK クエリで特定のデータをいつ再フェッチするかを定義できます。これにより、データを手動で更新することなく、キャッシュを最新の状態に保つことができます。
- 自動生成フック: RTK クエリは各 API エンドポイントのフックを作成し、単純な React フック (useGetPostsQuery、useCreatePostMutation など) を使用して API を呼び出すことができます。
- エラー処理: ミドルウェアによるカスタム エラー処理が含まれており、エラーの捕捉と表示が簡単になります。
- 簡素化された Redux 統合: RTK クエリは Redux と直接統合されるため、グローバルな状態管理やキャッシュに追加のライブラリは必要ありません。
RTK クエリと React クエリの比較
React Query と RTK Query はどちらも、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 内で集中エラー処理と devtools を統合する必要があります。
-
次の場合に React Query を使用します:
- Redux に依存しない、より軽量なセットアップが必要です。
- 個別のサーバー状態管理を好み、グローバルなアプリ状態は必要ありません。
本質的に、RTK Query は Redux 中心のアプリケーションに優れていますが、React Query は 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 ストア (src/store/store.js): Redux ストアは、アプリケーションの状態を保持する主要な構造です。セットアップでは、Redux 状態の特定の部分をローカルに保存する redux-persist によって拡張され、アプリが再起動しても保持されます。
-
redux-persist:
- 目的: Redux 状態の一部をアプリセッション間で永続的に維持するのに役立ちます。
- 構成:persistConfig オブジェクトは、auth、postApi、および usersApi を永続化 (ブラックリストに登録) しないように指定します。つまり、アプリの再起動時にデータがリセットされます。
- persistReducer は、Reducer 構成と永続化機能を組み合わせたものです。
エンハンサー: カスタム エンハンサーは、開発モードで Reactotron を統合するために使用されます。これは、Redux アクション、状態、およびネットワーク リクエストをデバッグするための便利なツールです。これは開発時のみ有効となり、運用環境に影響を与えることなくデバッグが容易になります。
-
ミドルウェア:
- 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 ヘッダーを設定します。
- Headers: 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 リクエストの監視、ログの検査に役立つデバッグ ツールです。
- Setup: 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'; 輸入 { アクティビティインジケーター、 ボタン、 フラットリスト、 モーダル、 リフレッシュコントロール、 スタイルシート、 文章、 テキスト入力、 ビュー、 「反応ネイティブ」から; import { SafeAreaView } から 'react-native-safe-area-context'; import { useDispatch, useSelector } から 'react-redux'; import { useLoginMutation } から './api/authApi'; 輸入 { useCreatePostMutation、 useDeletePostMutation、 useGetPostsQuery、 useLazyGetPostsQuery、 UpdatePostMutation を使用する、 './api/postsApi' から; import { useGetUserProfileQuery } から './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 token = useSelector((state) => state.auth.token); // ログインの突然変異 const [login, { isLoading: isLoggingIn }] = useLoginMutation(); // トークンが利用可能な場合にユーザー プロファイルを取得します const { データ: userProfile, refetch: refetchUserProfile } = useGetUserProfileQuery(未定義, { スキップ: !トークン、 }); // ページ分割された投稿を取得します 定数{ データ: 投稿、 読み込み中、 フェッチ中、 エラーです、 再フェッチ、 } = 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, ...posts])); } }, [投稿、ページ]); // ログインハンドラ const handleLogin = async () => { 試す { const credentials = { ユーザー名: 'emilys'、パスワード: 'emilyspass' }; ログインを待ちます(認証情報); console.log('ユーザープロファイル', ユーザープロファイル); refetchUserProfile(); } キャッチ (エラー) { console.error('ログインに失敗しました:', エラー); } }; const handleRefresh = async () => { setRefreshing(true); setPage(1); // 次のスクロールのためにページを 1 にリセットします setPostsData([]); // 重複を避けるためにデータをクリアします // 最初のページのフェッチを明示的にトリガーします const { data } = awaittriggerFetchFirstPage({ ページ: 1, 制限: 10 }); if (データ) { setPostsData(データ); // 投稿データを最初のページの結果に設定します } setRefreshing(false); }; // 新しい投稿を作成して先頭に追加し、リストを再取得します const handleCreatePost = async () => { if (newPostTitle) { const { data: newPost } = await createPost({ title: newPostTitle, body: '新規投稿コンテンツ' }); setNewPostTitle(''); setPostsData((prevData) => [newPost, ...prevData]); 再フェッチ(); } }; // 既存の投稿を更新し、タイトルに「HASAN」を追加します const handleUpdatePost = async (post) => { const { データ: updatedPost } = updatePostを待ちます({ id: post.id、 タイトル: `${post.title} ハサン`, }); setPostsData((prevData) => prevData.map((item) => (item?.id === updatedPost?.id ? updatedPost : item)) ); }; // 投稿を削除し、すぐに UI から削除します const handleDeletePost = async (id) => { deletePost(id) を待ちます; setPostsData((prevData) => prevData.filter((post) => post.id !== id)); }; // 無限スクロールのためにさらに投稿を読み込みます constloadMorePosts = () => { if (!isFetching) { setPage((前のページ) => 前のページ 1); } }; // モーダルの可視性を切り替えます const toggleModal = () => { setModalVisible(!isModalVisible); }; if (isLoading && page === 1) return <text>Loading...</text>; if (isError) return <text>投稿の取得中にエラーが発生しました。</text>; 戻る (
-
MainApp コンポーネント (src/MainApp.js):
- 状態とフック: ローカル状態 (投稿のページネーションなど) と、特定のイベントでアクションをトリガーする useLoginMutation などのフックを管理します。
-
ログイン:
- useLoginMutation を使用してユーザーをログインさせ、refetchUserProfile をトリガーしてユーザー プロファイル データをロードします。
- 条件付きクエリ: 有効なトークンが存在する場合にのみユーザー プロファイルを取得し (スキップ: !token)、不要な API 呼び出しを減らします。
-
投稿を取得しています:
- useGetPostsQuery を使用してページ分割された投稿を取得し、ユーザーがスクロールするにつれてより多くのデータを取得することで無限スクロールをサポートします。
- 更新コントロール: ユーザーが投稿リストを更新できるようにします。これは、モバイルでのプルツーリフレッシュ機能に役立ちます。
-
投稿の作成、更新、削除:
- Create: createPost を呼び出し、新しい投稿を先頭にして投稿リストを即座に更新します。
- 更新: 更新時に投稿のタイトルに「HASAN」を追加します。
- 削除: deletePost によるキャッシュの無効化により、ページをリロードすることなく投稿を削除し、UI を更新します。
-
UI 要素:
- モーダルにはユーザー プロフィールが表示されます。プロフィール ボタンは、userProfile データがロードされている場合にのみ表示され、ユーザー エクスペリエンスが向上します。
- FlatList: スクロール可能なページ分割された形式で投稿を表示し、使いやすさを高めます。
まとめ:
React Native アプリは、効率的なデータ管理と API 対話のために Redux Toolkit (RTK) Query を使用します。セットアップには以下が含まれます:
ストア構成: アプリセッション間で特定のデータを保存する redux-persist を備えた Redux ストア、エラーログ用のカスタムミドルウェア、開発モードでのデバッグ用の Reactotron。
-
RTK クエリを使用した API:
- authApi は、ログインの変更による認証を処理し、トークンを Redux に保存します。
- postsApi は、投稿の CRUD 操作を提供し、投稿の追加、更新、または削除時にキャッシュ タグを使用してデータを自動的に更新します。
- usersApi は、動的なトークンベースの認証ヘッダーを含むユーザー プロファイルを取得します。
認証スライス: 認証トークンを管理し、ログイン/ログアウト時にトークンを設定またはクリアするためのアクションを提供します。
-
アプリと MainApp コンポーネント:
- メイン アプリは Provider と PersistGate でコンポーネントをラップし、レンダリング前に状態が確実に読み込まれるようにします。
- MainApp は、投稿の取得、作成、更新、削除を管理します。条件付きでデータを読み込み (例: トークンが存在する場合にのみユーザー プロフィールを取得)、ページネーションと無限スクロールをサポートします
- ページ分割された投稿リストには FlatList、プロフィールにはモーダル、すっきりと整理されたレイアウトには基本スタイルを使用します。
完全なコード->
以上がRTK クエリを使用した React Native での効率的なデータ処理の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

JavaScript文字列置換法とFAQの詳細な説明 この記事では、javaScriptの文字列文字を置き換える2つの方法について説明します:内部JavaScriptコードとWebページの内部HTML。 JavaScriptコード内の文字列を交換します 最も直接的な方法は、置換()メソッドを使用することです。 str = str.replace( "find"、 "置換"); この方法は、最初の一致のみを置き換えます。すべての一致を置き換えるには、正規表現を使用して、グローバルフラグGを追加します。 str = str.replace(/fi

それで、あなたはここで、Ajaxと呼ばれるこのことについてすべてを学ぶ準備ができています。しかし、それは正確には何ですか? Ajaxという用語は、動的でインタラクティブなWebコンテンツを作成するために使用されるテクノロジーのゆるいグループ化を指します。 Ajaxという用語は、もともとJesse Jによって造られました

10の楽しいjQueryゲームプラグインして、あなたのウェブサイトをより魅力的にし、ユーザーの粘着性を高めます! Flashは依然としてカジュアルなWebゲームを開発するのに最適なソフトウェアですが、jQueryは驚くべき効果を生み出すこともできます。また、純粋なアクションフラッシュゲームに匹敵するものではありませんが、場合によってはブラウザで予期せぬ楽しみもできます。 jquery tic toeゲーム ゲームプログラミングの「Hello World」には、JQueryバージョンがあります。 ソースコード jQueryクレイジーワードコンポジションゲーム これは空白のゲームであり、単語の文脈を知らないために奇妙な結果を生み出すことができます。 ソースコード jquery鉱山の掃引ゲーム

記事では、JavaScriptライブラリの作成、公開、および維持について説明し、計画、開発、テスト、ドキュメント、およびプロモーション戦略に焦点を当てています。

このチュートリアルでは、jQueryを使用して魅惑的な視差の背景効果を作成する方法を示しています。 見事な視覚的な深さを作成するレイヤー画像を備えたヘッダーバナーを構築します。 更新されたプラグインは、jQuery 1.6.4以降で動作します。 ダウンロードしてください

この記事では、ブラウザでJavaScriptのパフォーマンスを最適化するための戦略について説明し、実行時間の短縮、ページの負荷速度への影響を最小限に抑えることに焦点を当てています。

Matter.jsは、JavaScriptで書かれた2D Rigid Body Physics Engineです。このライブラリは、ブラウザで2D物理学を簡単にシミュレートするのに役立ちます。剛体を作成し、質量、面積、密度などの物理的特性を割り当てる機能など、多くの機能を提供します。また、重力摩擦など、さまざまな種類の衝突や力をシミュレートすることもできます。 Matter.jsは、すべての主流ブラウザをサポートしています。さらに、タッチを検出し、応答性が高いため、モバイルデバイスに適しています。これらの機能はすべて、物理ベースの2Dゲームまたはシミュレーションを簡単に作成できるため、エンジンの使用方法を学ぶために時間をかける価値があります。このチュートリアルでは、このライブラリのインストールや使用法を含むこのライブラリの基本を取り上げ、

この記事では、JQueryとAjaxを使用して5秒ごとにDivのコンテンツを自動的に更新する方法を示しています。 この例は、RSSフィードからの最新のブログ投稿と、最後の更新タイムスタンプを取得して表示します。 読み込み画像はオプションです


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

EditPlus 中国語クラック版
サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

ZendStudio 13.5.1 Mac
強力な PHP 統合開発環境

VSCode Windows 64 ビットのダウンロード
Microsoft によって発売された無料で強力な IDE エディター

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

Dreamweaver Mac版
ビジュアル Web 開発ツール

ホットトピック



