ホームページ >ウェブフロントエンド >jsチュートリアル >RTK クエリを使用した React Native での効率的なデータ処理

RTK クエリを使用した React Native での効率的なデータ処理

Mary-Kate Olsen
Mary-Kate Olsenオリジナル
2024-11-30 10:01:14939ブラウズ

このガイドでは以下について説明します:

  • CRUD オペレーション
  • ページネーション
  • RTK クエリを使用した Redux 永続化
  • 複数のベース URL の使用
  • 保護された公共ルート
  • キャッシュの管理と無効化

Efficient Data Handling in React Native with RTK Query

RTK Query は、Redux Toolkit (RTK) に組み込まれた高度なデータフェッチおよびキャッシュ ツールです。データのフェッチ、キャッシュ、更新などの一般的なタスク用の Redux スライスとフックを生成することで、API の対話を合理化します。主な機能は次のとおりです:

  1. 自動キャッシュ: RTK クエリはデータをキャッシュし、データが無効になったときに自動的に再取得して、UI に常に最新のデータが含まれるようにします。
  2. キャッシュの無効化: タグを使用すると、RTK クエリで特定のデータをいつ再フェッチするかを定義できます。これにより、データを手動で更新することなく、キャッシュを最新の状態に保つことができます。
  3. 自動生成フック: RTK クエリは各 API エンドポイントのフックを作成し、単純な React フック (useGetPostsQuery、useCreatePostMutation など) を使用して API を呼び出すことができます。
  4. エラー処理: ミドルウェアによるカスタム エラー処理が含まれており、エラーの捕捉と表示が簡単になります。
  5. 簡素化された Redux 統合: RTK クエリは Redux と直接統合されるため、グローバルな状態管理やキャッシュに追加のライブラリは必要ありません。

RTK クエリと React クエリの比較

React QueryRTK 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 を使用しないプロジェクトや、よりローカライズされたサーバー状態管理に焦点を当てたプロジェクトに柔軟性とシンプルさを提供します。


Efficient Data Handling in React Native with RTK Query



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 は手動介入なしで新しいデータを取得するように求められます。

// 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 を使用します。セットアップには以下が含まれます:

  1. ストア構成: アプリセッション間で特定のデータを保存する redux-persist を備えた Redux ストア、エラーログ用のカスタムミドルウェア、開発モードでのデバッグ用の Reactotron。

  2. RTK クエリを使用した API:

    • authApi は、ログインの変更による認証を処理し、トークンを Redux に保存します。
    • postsApi は、投稿の CRUD 操作を提供し、投稿の追加、更新、または削除時にキャッシュ タグを使用してデータを自動的に更新します。
    • usersApi は、動的なトークンベースの認証ヘッダーを含むユーザー プロファイルを取得します。
  3. 認証スライス: 認証トークンを管理し、ログイン/ログアウト時にトークンを設定またはクリアするためのアクションを提供します。

  4. アプリと MainApp コンポーネント:

    • メイン アプリは Provider と PersistGate でコンポーネントをラップし、レンダリング前に状態が確実に読み込まれるようにします。
    • MainApp は、投稿の取得、作成、更新、削除を管理します。条件付きでデータを読み込み (例: トークンが存在する場合にのみユーザー プロフィールを取得)、ページネーションと無限スクロールをサポートします
    • ページ分割された投稿リストには FlatList、プロフィールにはモーダル、すっきりと整理されたレイアウトには基本スタイルを使用します。

完全なコード->

以上がRTK クエリを使用した React Native での効率的なデータ処理の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。