博主头像
<CodeEra />

心存敬畏 行有所止

Redux-Toolkit 学习笔记:简化 Redux 的终极方案

1. Redux-Toolkit 简介

Redux-Toolkit (RTK) 是 Redux 官方推荐的简化 Redux 开发的工具集,它提供了一系列工具来简化常见的 Redux 使用模式,包括配置 store、创建 reducer 和 actions 等。

主要优点

  • 简化 Redux 配置
  • 减少样板代码
  • 内置最佳实践
  • 集成 Immer 和 Redux-Thunk

2. 核心 API

2.1 configureStore

替代传统的 createStore,提供简化的配置方式:

import { configureStore } from '@reduxjs/toolkit';

const store = configureStore({
  reducer: {
    // 添加你的 reducers 这里
    posts: postsReducer,
    comments: commentsReducer,
    users: usersReducer,
  },
  // 可选配置
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
  devTools: process.env.NODE_ENV !== 'production',
  preloadedState: initialState,
});

2.2 createSlice

自动生成 action creators 和 action types,简化 reducer 创建:

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

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    increment: (state) => state + 1,
    decrement: (state) => state - 1,
    incrementByAmount: (state, action) => state + action.payload,
  },
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;

2.3 createAsyncThunk

简化异步操作处理:

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

export const fetchUserById = createAsyncThunk(
  'users/fetchByIdStatus',
  async (userId, thunkAPI) => {
    const response = await userAPI.fetchById(userId);
    return response.data;
  }
);

2.4 createEntityAdapter

提供标准化的数据存储和操作:

import { createEntityAdapter } from '@reduxjs/toolkit';

const usersAdapter = createEntityAdapter({
  selectId: (user) => user.id,
  sortComparer: (a, b) => a.name.localeCompare(b.name),
});

3. 实际应用示例

3.1 创建 Store

// store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import usersReducer from '../features/users/usersSlice';

export default configureStore({
  reducer: {
    counter: counterReducer,
    users: usersReducer,
  },
});

3.2 创建 Slice

// postsSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { fetchPosts } from './api';

export const getPosts = createAsyncThunk('posts/getPosts', async () => {
  const response = await fetchPosts();
  return response.data;
});

const postsSlice = createSlice({
  name: 'posts',
  initialState: {
    items: [],
    status: 'idle',
    error: null,
  },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(getPosts.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(getPosts.fulfilled, (state, action) => {
        state.status = 'succeeded';
        state.items = action.payload;
      })
      .addCase(getPosts.rejected, (state, action) => {
        state.status = 'failed';
        state.error = action.error.message;
      });
  },
});

export default postsSlice.reducer;

3.3 在组件中使用

import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { getPosts } from './postsSlice';

function PostsList() {
  const dispatch = useDispatch();
  const posts = useSelector((state) => state.posts.items);
  const status = useSelector((state) => state.posts.status);
  const error = useSelector((state) => state.posts.error);

  useEffect(() => {
    if (status === 'idle') {
      dispatch(getPosts());
    }
  }, [status, dispatch]);

  if (status === 'loading') return <div>Loading...</div>;
  if (status === 'failed') return <div>Error: {error}</div>;

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

4. 最佳实践

  1. 使用 RTK Query 处理数据获取:对于数据获取和缓存,优先考虑使用 RTK Query
  2. 按功能组织代码:将相关 slice、actions 和 selectors 放在同一文件中
  3. 使用 Immer 进行不可变更新:RTK 内置了 Immer,可以直接"修改"state
  4. 避免在 reducer 中执行副作用:将异步逻辑放在 thunks 中
  5. 使用 TypeScript:RTK 对 TypeScript 有很好的支持

5. RTK Query

RTK Query 是 Redux Toolkit 中包含的数据获取和缓存解决方案:

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

export const apiSlice = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => '/posts',
    }),
    addNewPost: builder.mutation({
      query: (post) => ({
        url: '/posts',
        method: 'POST',
        body: post,
      }),
    }),
  }),
});

export const { useGetPostsQuery, useAddNewPostMutation } = apiSlice;

6. 总结

Redux Toolkit 极大地简化了 Redux 的使用,减少了样板代码,内置了最佳实践,使得 Redux 更加易于使用和维护。对于新项目,强烈建议使用 Redux Toolkit 而不是传统的 Redux。

发表新评论