Skip to content

React + Redux + Toolkit

介绍

Redux Toolkit 是一组工具,它为 Redux 开发者提供一系列的功能,包括创建 Redux store、reducers、actions、selectors、middleware、and more。它可以帮助你更有效地编写 Redux 应用,并减少样板代码。

特点

  • 集成了 Redux 最佳实践,包括 Redux 核心、Redux DevTools、Immer、Thunk 等。
  • 提供了创建 Redux store 的便捷函数,包括创建 store、添加 middleware、添加 reducer、创建 action。
  • 提供了创建 reducer 的便捷函数,包括创建 reducer、添加 action 类型、添加默认值。
  • 提供了创建 action 的便捷函数,包括创建 action、添加 action 类型、添加 payload。
  • 提供了创建 selector 的便捷函数,包括创建 selector、添加输入参数、添加默认值。
  • 提供了 Redux 异步操作的便捷函数,包括创建异步 action、创建异步 reducer、创建异步 selector。
  • 提供了 Redux 工具函数,包括创建 Redux store、创建 Redux middleware、创建 Redux enhancer。
  • 提供了 Redux 相关的工具,包括 Redux DevTools、Immer、Thunk 等。

安装

npm install redux react-redux @reduxjs/toolkit

基本用法

创建slice

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

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

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

export default counterSlice.reducer;

创建store

javascript
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';


const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

export default store;

使用store

javascript
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';

function Counter() {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
    </div>
  );
}


export default Counter;

异步操作

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

const fetchUserById = createAsyncThunk(
  'users/fetchUserById',
  async (userId) => {
    const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
    return response.json();
  }
);


const userSlice = createSlice({
  name: 'users',
  initialState: {
    entities: {},
    ids: [],
    loading: 'idle',
    error: null,
  },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUserById.pending, (state) => {
         state.loading = 'pending';
       })
      .addCase(fetchUserById.fulfilled, (state, action) => {
         state.entities[action.payload.id] = action.payload;
         state.ids.push(action.payload.id);
         state.loading = 'idle';
       })
      .addCase(fetchUserById.rejected, (state, action) => {
         state.loading = 'idle';
         state.error = action.error.message;
       });
  },
});

export const { fetchUserById } = userSlice.actions;

export default userSlice.reducer;
javascript
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchUserById } from './userSlice';

function User() {
  const dispatch = useDispatch();
  const { loading, error, entities } = useSelector((state) => state.users);
  const user = entities[1];

  return (
    <div>
      {loading === 'pending' && <div>Loading...</div>}
      {error && <div>{error}</div>}
      {user && <div>{user.name}</div>}
      <button onClick={() => dispatch(fetchUserById(1))}>Load user</button>
    </div>
  );
}

export default User;