このガイドでは以下について説明します:
- 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 サイトの他の関連記事を参照してください。

PythonとJavaScriptの主な違いは、タイプシステムとアプリケーションシナリオです。 1。Pythonは、科学的コンピューティングとデータ分析に適した動的タイプを使用します。 2。JavaScriptは弱いタイプを採用し、フロントエンドとフルスタックの開発で広く使用されています。この2つは、非同期プログラミングとパフォーマンスの最適化に独自の利点があり、選択する際にプロジェクトの要件に従って決定する必要があります。

PythonまたはJavaScriptを選択するかどうかは、プロジェクトの種類によって異なります。1)データサイエンスおよび自動化タスクのPythonを選択します。 2)フロントエンドとフルスタック開発のためにJavaScriptを選択します。 Pythonは、データ処理と自動化における強力なライブラリに好まれていますが、JavaScriptはWebインタラクションとフルスタック開発の利点に不可欠です。

PythonとJavaScriptにはそれぞれ独自の利点があり、選択はプロジェクトのニーズと個人的な好みに依存します。 1. Pythonは、データサイエンスやバックエンド開発に適した簡潔な構文を備えた学習が簡単ですが、実行速度が遅くなっています。 2。JavaScriptはフロントエンド開発のいたるところにあり、強力な非同期プログラミング機能を備えています。 node.jsはフルスタックの開発に適していますが、構文は複雑でエラーが発生しやすい場合があります。

javascriptisnotbuiltoncorc;それは、解釈されていることを解釈しました。

JavaScriptは、フロントエンドおよびバックエンド開発に使用できます。フロントエンドは、DOM操作を介してユーザーエクスペリエンスを強化し、バックエンドはnode.jsを介してサーバータスクを処理することを処理します。 1.フロントエンドの例:Webページテキストのコンテンツを変更します。 2。バックエンドの例:node.jsサーバーを作成します。

PythonまたはJavaScriptの選択は、キャリア開発、学習曲線、エコシステムに基づいている必要があります。1)キャリア開発:Pythonはデータサイエンスとバックエンド開発に適していますが、JavaScriptはフロントエンドおよびフルスタック開発に適しています。 2)学習曲線:Python構文は簡潔で初心者に適しています。 JavaScriptの構文は柔軟です。 3)エコシステム:Pythonには豊富な科学コンピューティングライブラリがあり、JavaScriptには強力なフロントエンドフレームワークがあります。

JavaScriptフレームワークのパワーは、開発を簡素化し、ユーザーエクスペリエンスとアプリケーションのパフォーマンスを向上させることにあります。フレームワークを選択するときは、次のことを検討してください。1。プロジェクトのサイズと複雑さ、2。チームエクスペリエンス、3。エコシステムとコミュニティサポート。

はじめに私はあなたがそれを奇妙に思うかもしれないことを知っています、JavaScript、C、およびブラウザは正確に何をしなければなりませんか?彼らは無関係であるように見えますが、実際、彼らは現代のウェブ開発において非常に重要な役割を果たしています。今日は、これら3つの間の密接なつながりについて説明します。この記事を通して、JavaScriptがブラウザでどのように実行されるか、ブラウザエンジンでのCの役割、およびそれらが協力してWebページのレンダリングと相互作用を駆動する方法を学びます。私たちは皆、JavaScriptとブラウザの関係を知っています。 JavaScriptは、フロントエンド開発のコア言語です。ブラウザで直接実行され、Webページが鮮明で興味深いものになります。なぜJavascrを疑問に思ったことがありますか


ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

DVWA
Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

PhpStorm Mac バージョン
最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール

MinGW - Minimalist GNU for Windows
このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

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

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