ホームページ > ウェブフロントエンド > jsチュートリアル > React 開発者向けの包括的な Redux ツールキットのメモ

React 開発者向けの包括的な Redux ツールキットのメモ

Barbara Streisand
リリース: 2025-01-15 07:37:43
オリジナル
213 人が閲覧しました

Comprehensive Redux Toolkit Notes for React Developers

? Redux ツールキットのメモ ?

Redux とは何ですか?
Redux は、アプリケーションの状態を個別に管理する JS アプリ用の柔軟な状態コンテナーです。アプリケーションの状態を 1 つのストアで管理するため、アプリ全体にわたる複雑な状態ロジックの処理が容易になります。

Redux を使用する理由
通常のフローでは、コンポーネント間で状態を渡すためにプロップドリルを行う必要があります。一部のレベルではここでの状態を必要としませんが、これは負担です。また、大規模な中型アプリの状態を引き上げるには構造的な変更が必要なため、スケーラブルなソリューションではありません。だからこそ、状態を管理するために redux が必要なのです。ここでのすべての状態はストアに保持され、必要なコンポーネントはそのストアにサブスクライブするだけで済みます。 Redux は、一方向のデータ フローを強制することで、予測可能な状態管理、デバッグの容易化、スケーラビリティの向上を保証します。

コア Redux コンポーネント:

アクション: 何が起こったかを説明するオブジェクト。通常、これにはタイプとオプションのペイロードが含まれます。 (コマンド)
Dispatch: 状態を更新するためにストアにアクションを送信するために使用される関数。 (イベント発生中)
Reducer: 現在の状態とアクションを受け取り、新しい状態を返す純粋な関数。 (アクションのディスパッチ時にトリガーされる関数)

インストール: npm i @reduxjs/toolkit reverse-redux

Redux ワークフロー:

スライスの作成:
スライスは、単一の機能に対する Redux Reducer ロジックとアクションのコレクションです。 prepare コールバックを使用すると、アクション ペイロードがリデューサーに到達する前にカスタマイズできます。

import { createSlice, nanoid } from "@reduxjs/toolkit";

const postSlice = createSlice({
 name: "posts",
 initialState: [],
 reducers: {
   addPost: {
     reducer: (state, action) => {
       state.push(action.payload);
     },
     prepare: (title, content) => ({
       payload: { id: nanoid(), title, content },
     }),
   },
   deletePost: (state, action) => {
     return state.filter((post) => post.id != action.payload);
   },
 },
});

export const { addPost, deletePost } = postSlice.actions;

export default postSlice.reducer;
ログイン後にコピー
ログイン後にコピー

ストアの作成:

import { configureStore } from "@reduxjs/toolkit";
import postReducer from "../features/posts/postSlice";

export const store = configureStore({
   reducer: {
       posts: postReducer
   },
 });

ログイン後にコピー
ログイン後にコピー

プロバイダーでラップ:

import { Provider } from "react-redux";
import { store } from "./app/store.jsx";

createRoot(document.getElementById("root")).render(
 <StrictMode>
   <Provider store={store}>
     <App />
   </Provider>
 </StrictMode>
);
ログイン後にコピー
ログイン後にコピー

コンポーネントで使用:

const PostList = ({ onEdit }) => {
 const posts = useSelector((state) => state.posts);
 const dispatch = useDispatch();

 return (
   <div className="w-full grid grid-cols-1 gap-6 mt-12">
     {posts.map((post) => (
       <div key={post.id}></div>
     ))}
   </div>
 );
};
ログイン後にコピー
ログイン後にコピー

Redux ブラウザ拡張機能: Redux DevTools

const store = configureStore({
  reducer: rootReducer,
  devTools: process.env.NODE_ENV !== 'production',
});
ログイン後にコピー
ログイン後にコピー

Redux での非同期操作 (Redux サンク):

Redux では、非同期操作 (API 呼び出しなど) はミドルウェアを使用して処理されます。これは、Redux がデフォルトで同期状態更新のみをサポートしているためです。非同期操作を処理するための最も一般的なミドルウェアは、Redux Thunk、createAsyncThunk を備えた Redux Toolkit (RTK)、および Redux Saga です。

実装:

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

// Fetch all posts
export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts');
  return response.json();
});

// Initial State
const initialState = {
  posts: [],
  post: null,
  loading: false,
  error: null,
};

// Slice
const postsSlice = createSlice({
  name: 'posts',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      // Fetch all posts
      .addCase(fetchPosts.pending, (state) => {
        state.loading = true;
      })
      .addCase(fetchPosts.fulfilled, (state, action) => {
        state.loading = false;
        state.posts = action.payload;
      })
      .addCase(fetchPosts.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message;
      })

      },
});

export default postsSlice.reducer;
ログイン後にコピー
ログイン後にコピー

使用例:

import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchPosts, createPost, updatePost, deletePost } from './postsSlice';

const Posts = () => {
  const dispatch = useDispatch();
  const { posts, loading, error } = useSelector((state) =>state.posts);

  useEffect(() => {
    dispatch(fetchPosts());
  }, [dispatch]);

  const handleCreate = () => {
    dispatch(createPost({ title: 'New Post', body: 'This is a new post' }));
  };

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <div>
      <h1>Posts</h1>
      <button onClick={handleCreate}>Create Post</button>
     </div>
  );
};

export default Posts;
ログイン後にコピー
ログイン後にコピー

ミドルウェア
Redux のミドルウェアはディスパッチされたアクションをインターセプトし、ロギング、クラッシュ レポート、または非同期ロジックの処理を可能にします。ミドルウェアを使用すると、ディスパッチプロセスをカスタマイズできます。

const blogPostMiddleware = (storeAPI) => (next) => (action) => {
  if (action.type === 'posts/publishPost') {
    const contentLength = action.payload.content.length;

    if (contentLength < 50) {
      console.warn('Post content is too short. Must be at least 50 characters.');
      return;
    }
    console.log('Publishing post:', action.payload.title);
  }
  return next(action);
};

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(blogPostMiddleware),
});
ログイン後にコピー
ログイン後にコピー

セレクター
セレクターは、状態の特定の部分にアクセスするのに役立ちます。

export const selectCount = (state) => state.counter.value;

エラー処理
適切な状態管理によりエラーを効果的に処理します。

import { createSlice, nanoid } from "@reduxjs/toolkit";

const postSlice = createSlice({
 name: "posts",
 initialState: [],
 reducers: {
   addPost: {
     reducer: (state, action) => {
       state.push(action.payload);
     },
     prepare: (title, content) => ({
       payload: { id: nanoid(), title, content },
     }),
   },
   deletePost: (state, action) => {
     return state.filter((post) => post.id != action.payload);
   },
 },
});

export const { addPost, deletePost } = postSlice.actions;

export default postSlice.reducer;
ログイン後にコピー
ログイン後にコピー

RTK クエリ (簡易データ取得)

RTK クエリは、データのフェッチ、キャッシュ、同期を簡素化します。 RTK クエリはリクエストを自動的にキャッシュし、不必要な再フェッチを回避してパフォーマンスを向上させます。

RTK クエリのセットアップ

import { configureStore } from "@reduxjs/toolkit";
import postReducer from "../features/posts/postSlice";

export const store = configureStore({
   reducer: {
       posts: postReducer
   },
 });

ログイン後にコピー
ログイン後にコピー

コンポーネントでの使用法

import { Provider } from "react-redux";
import { store } from "./app/store.jsx";

createRoot(document.getElementById("root")).render(
 <StrictMode>
   <Provider store={store}>
     <App />
   </Provider>
 </StrictMode>
);
ログイン後にコピー
ログイン後にコピー

Immer による不変の更新

Immer を使用すると、内部で更新を不変に保ちながら、状態を直接「変更」するロジックを作成できます。

const PostList = ({ onEdit }) => {
 const posts = useSelector((state) => state.posts);
 const dispatch = useDispatch();

 return (
   <div className="w-full grid grid-cols-1 gap-6 mt-12">
     {posts.map((post) => (
       <div key={post.id}></div>
     ))}
   </div>
 );
};
ログイン後にコピー
ログイン後にコピー

突然変異と不変

Mutate: データを直接変更します。たとえば、オブジェクトまたは配列を変更します。
不変: データを直接変更する代わりに、元のデータはそのままにして、変更を適用した新しいコピーを作成します。

Immer の仕組み
Immer は、データを変更している (つまり、データを直接変更している) ように見えるコードを作成するのに役立ちますが、内部では自動的に変更を不変に保ちます。これは、JavaScript で不変のデータ構造を扱うときによくあるバグを回避するのに役立ちます。
例: Immer なし (突然変異):

const store = configureStore({
  reducer: rootReducer,
  devTools: process.env.NODE_ENV !== 'production',
});
ログイン後にコピー
ログイン後にコピー

Immer あり (不変性):

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

// Fetch all posts
export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts');
  return response.json();
});

// Initial State
const initialState = {
  posts: [],
  post: null,
  loading: false,
  error: null,
};

// Slice
const postsSlice = createSlice({
  name: 'posts',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      // Fetch all posts
      .addCase(fetchPosts.pending, (state) => {
        state.loading = true;
      })
      .addCase(fetchPosts.fulfilled, (state, action) => {
        state.loading = false;
        state.posts = action.payload;
      })
      .addCase(fetchPosts.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message;
      })

      },
});

export default postsSlice.reducer;
ログイン後にコピー
ログイン後にコピー

これにより、手動で状態を複製して更新する必要がなくなるため、Redux (またはその他の状態管理) の操作が容易になります。 Immer はそれを自動的に実行します。

Redux 永続化:

ページが更新されても Redux 状態を維持するには、Redux Persist を統合します。これにより、Redux の状態がローカル ストレージまたはセッション ストレージに保存され、アプリが更新されたときに再ロードされます。

インストール:
npm install redux-persist

実装:

import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchPosts, createPost, updatePost, deletePost } from './postsSlice';

const Posts = () => {
  const dispatch = useDispatch();
  const { posts, loading, error } = useSelector((state) =>state.posts);

  useEffect(() => {
    dispatch(fetchPosts());
  }, [dispatch]);

  const handleCreate = () => {
    dispatch(createPost({ title: 'New Post', body: 'This is a new post' }));
  };

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <div>
      <h1>Posts</h1>
      <button onClick={handleCreate}>Create Post</button>
     </div>
  );
};

export default Posts;
ログイン後にコピー
ログイン後にコピー

パーシットゲートでラップ:

const blogPostMiddleware = (storeAPI) => (next) => (action) => {
  if (action.type === 'posts/publishPost') {
    const contentLength = action.payload.content.length;

    if (contentLength < 50) {
      console.warn('Post content is too short. Must be at least 50 characters.');
      return;
    }
    console.log('Publishing post:', action.payload.title);
  }
  return next(action);
};

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(blogPostMiddleware),
});
ログイン後にコピー
ログイン後にコピー

オプションの拡張機能

localStorage の代わりに sessionStorage を使用します:
ストレージをセッションベースに変更します (ブラウザを閉じるとクリアされます):

initialState: {
  items: [],
  status: 'idle',
  error: null,
},

.addCase(fetchData.rejected, (state, action) => {
  state.status = 'failed';
  state.error = action.error.message;
});
ログイン後にコピー

選択的永続性:
状態の特定のスライスのみを永続化します:

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

const api = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://jsonplaceholder.typicode.com' }),
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => '/posts',
    }),
    getPostById: builder.query({
      query: (id) => `/posts/${id}`,
    }),
    createPost: builder.mutation({
      query: (newPost) => ({
        url: '/posts',
        method: 'POST',
        body: newPost,
      }),
    }),
    updatePost: builder.mutation({
      query: ({ id, ...updatedPost }) => ({
        url: `/posts/${id}`,
        method: 'PUT',
        body: updatedPost,
      }),
    }),
    deletePost: builder.mutation({
      query: (id) => ({
        url: `/posts/${id}`,
        method: 'DELETE',
      }),
    }),
  }),
});

export const {
  useGetPostsQuery,
  useGetPostByIdQuery,
  useCreatePostMutation,
  useUpdatePostMutation,
  useDeletePostMutation,
} = api;
export default api;
ログイン後にコピー

CRUD 機能を備えた React、redux、ant デザインを使用してシンプルなブログ プロジェクトを作成しました。ぜひチェックしてみてください。
プロジェクト リンク - Redux ブログ アプリ

? Redux Toolkit をマスターして React アプリをレベルアップしましょう!

以上がReact 開発者向けの包括的な Redux ツールキットのメモの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート