React学习笔记:利用RTK优化Redux和配置请求(五)

668 阅读4分钟

无Hook版最原生的Redux十分复杂且麻烦,对于一个reducer需要根据传入的ActionType来执行对应逻辑。这不仅编写困难,维护也麻烦,最直观的感受就是不想再看这个代码一眼!
最新的Redux Tookit极大程度上简化逻辑处理。
React-Redux官方文档
Redux Tookit
本篇记录个人学习Redux踩过的坑及学习路线。


学习路线

理解早版本的React Redux。

对于早一些版本的项目大多采用的是原生ReactRedux,利用提供的Api进行数据读取和存放。这里不一定要完全掌握,只要理解其原理和使用方法便可。毕竟RTK实在太过便利。

学习Redux搭配RTK api

RTK提供很多便利的API可以帮助我们将每个reducer转化成独立的逻辑切片。并允许我们以对象函数形式编写每个切片的处理逻辑。

学习RTK提供的异步处理方案和中间件

最新版的RTK不需要在store里显式标注middleware,RTK已经帮你自动注册。
RTK提供creatAsnycThunk和配套的extraReducer帮助我们更好的处理异步请求数据逻辑。

学习RTK Query

一个原生基于RKT本身的请求逻辑。功能十分强大,几乎涵盖了所有方面。目前正在学习中


RTK之configureStoreApi

仓库管理员
个人理解为汇总所有reducer逻辑的仓库。 所有reducer都需要在仓库注册后才能使用。

import { configureStore } from '@reduxjs/toolkit'

export const store = configureStore({
  reducer: {
      //这里面存放每个reducerSlice
  },
})

RTK之createSlice

仓库
对此仓库中的所有逻辑负责。

import { createSlice } from '@reduxjs/toolkit'

const initialState = { value: 0 }

const counterSlice = createSlice({
  //初始化仓库名称
  name: 'counter',
  //初始化数据
  initialState,
  /*
  *Action简化为对象形式。
  *直接调用state对数据进行修改。
  *同样可以接受一个payload参数
  */
  reducers: {
    increment(state) {
      state.value++
    },
    decrement(state) {
      state.value--
    },
    incrementByAmount(state, action) {
      state.value += action.payload
    },
  },
})
//暴露Action给外界
export const { increment, decrement, incrementByAmount } = counterSlice.actions
//暴露仓库给仓库管理员
export default counterSlice.reducer

RTK之useSelector和useDispatch

由RTK提供的hook式函数进行数据获取和修改。
每一个Action都应在dispatch中提交,每一份数据都应该在selector中获取。
import { useSelector, useDispatch } from 'react-redux'

useSelector

完全替代connectApi。
const data = useSelector(state => state.xxx)
useSelector接受一个state参数并返回响应式数据。

useDispatch

用于提交每一个对state数据进行修改的action。
useDispatch(Action(payLoad))

RTK之createAsyncThunk

createAsyncThunk接受三个参数,一个用于定义action的String,一个函数 payloadCreator,一个option对象。并返回一个builder用于对state进行修改。
假设这里有个fetchUser的requestAPI

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

对于接受createAsyncThunk的Api,RTK提供了三个类似promiseStatu的string内描述请求过程。 pendding fulfilled,rejected。并且会将其拼接到第一个参数后面。从而变成

  • pending'users/fetchUserById/pending'
  • fulfilled'users/fetchUserById/fulfilled'
  • rejected'users/fetchUserById/rejected' 对于第二个参数payloadCreator默认接受两个参数。
  • 第一个参数 args。若为一个参数默认直接接受,若需要传递多个参数需要师兄对象形式传递。
  • 第二个参数thunkapi。由RTK提供的api帮助你处理各种情况。比较常见的如错误处理rejectWithValue。 第三个参数则是一个option对象。若有需要可以在官方文档查看搭配。这里没怎么用到过暂时不记录。

最最最重要的一步。extraReducer处理,次对象里面每一个reducer期望返回的是一个promise对象包裹的数据。 并且和正常的action一样,接受一个(state,action)参数。
下面是一个登录小demo。
获取到createAsyncThunk返回的实例,在extraReducer中根据实例中的Statu来编写对应的action。

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

import { getCurrentUser } from '../../api/userAPI';
import { auth } from '../../utils/auth';

export const checkAuth = createAsyncThunk('signin/checkAuth', async () => {
  if (auth.isAuthenticated()) {
    const token = auth.getToken();
    const user = await getCurrentUser({ token });

    return { token, user };
  }

  return { token: null, user: null };
});

export const login = createAsyncThunk('signin/login', auth.login);

export const logout = createAsyncThunk('signin/logout', auth.logout);

const initialState = {
  loading: true,
  error: null,
  loggedIn: false,
  loggedInUser: null,
  token: null,
};

export const signinSlice = createSlice({
  name: 'signin',
  initialState,
  reducers: {},
  extraReducers: {
    [checkAuth.pending]: startLoading,
    [checkAuth.fulfilled]: (state, { payload }) => {
      const { token = null, user = null } = payload;

      Object.assign(state, {
        loading: false,
        error: null,
        loggedIn: !!token,
        loggedInUser: user,
        token,
      });
    },
    [checkAuth.rejected]: receiveError,

    [login.pending]: startLoading,
    [login.fulfilled]: (state, { payload }) => {
      const { token, user } = payload;

      Object.assign(state, {
        loading: false,
        loggedIn: true,
        loggedInUser: user,
        token,
      });
    },
    [login.rejected]: receiveError,

    [logout.pending]: startLoading,
    [logout.fulfilled]: (state) =>
      Object.assign(state, {
        ...initialState,
        loading: false,
      }),
    [logout.rejected]: receiveError,
  },
});

function startLoading(state) {
  Object.assign(state, {
    loading: true,
    error: null,
  });
}

function receiveError(state, action) {
  Object.assign(state, {
    loading: false,
    error: action.error,
  });
}

export const selectSignin = (state) => state.signin;

export const signinReducer = signinSlice.reducer;

学到这里略微有点迷茫,哎呦,不知道继续看RTK query还是使用其开源请求包。