react-redux的现代化用法 - 拥抱@reduxjs/toolkit

原创文章
声明:作者声明此文章为原创,未经作者同意,请勿转载,若转载,务必注明本站出处,本平台保留追究侵权法律责任的权利。
全栈老韩
全栈工程师,擅长iOS App开发、前端(vue、react、nuxt、小程序&Taro)开发、Flutter、React Native、后端(midwayjs、golang、express、koa)开发、docker容器、seo优化等。

首先我们来介绍一下这篇文章的主角:@reduxjs/toolkit,一个全新的react redux集合。
@reduxjs/toolkit诞生的原因,在官网(https://redux-toolkit.js.org/introduction/getting-started)上给出了明确的说法:

"Configuring a Redux store is too complicated"
"I have to add a lot of packages to get Redux to do anything useful"
"Redux requires too much boilerplate code"

简单的说,就是原来的react redux写法太复杂,并且需要添加更多的npm包去实现redux的逻辑,而且老版本中有一堆的模版代码写法。

那么新的@reduxjs/toolkit和旧的比,有哪些新颖写法,这篇文章对通用的几个写法做了一些对比。

对比

  • 老写法
    需要依赖:
package.json 复制代码
...
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0",
"redux": "^4.0.0",
"react-redux": "^7.2.0",
...
  1. store创建
store.ts 复制代码
import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux'
import thunkMiddleware from 'redux-thunk'
import logger from 'redux-logger'
import rootReducer from '../reducers'

const composeEnhancers = typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
  ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
    // Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
  })
  : compose

const middlewares = [
  thunkMiddleware
]

if (process.env.NODE_ENV === 'development') {
  middlewares.push(logger)
}

const enhancer = composeEnhancers(
  applyMiddleware(...middlewares),
  // other store enhancers if any
)

export default function configStore () {
  const store = createStore(rootReducer, enhancer)
  return store
}
  1. reducer集合
rootReducer.ts 复制代码
import { combineReducers } from 'redux'
import counter from './counter'

export default combineReducers({
  counter
})
  1. 声明方法case
constants/counter.ts 复制代码
export const ADD = 'ADD'
export const MINUS = 'MINUS'
  1. 声明action方法
actions/counter.ts 复制代码
import {
  ADD,
  MINUS
} from '../constants/counter'

export const add = () => {
  return {
    type: ADD
  }
}
export const minus = () => {
  return {
    type: MINUS
  }
}

// 异步的action
export function asyncAdd () {
  return dispatch => {
    setTimeout(() => {
      dispatch(add())
    }, 2000)
  }
}
  1. 具体的某一个reducer
counter.ts 复制代码
import { ADD, MINUS } from "../constants/counter";

const INITIAL_STATE = {
  num: 0,
};

export default function counter(state = INITIAL_STATE, action) {
  switch (action.type) {
    case ADD:
      return {
        ...state,
        num: state.num + 1,
      };
    case MINUS:
      return {
        ...state,
        num: state.num - 1,
      };
    default:
      return state;
  }
}
  1. 调用reducer
index.tsx 复制代码
...
// 第一种,component组件,如果你要将reducer绑定为组件的属性使用,那么要使用connect函数
export default connect(({ counter }) => ({
  counter
}), (dispatch) => ({
  add () {
    dispatch(add())
  },
  dec () {
    dispatch(minus())
  },
  asyncAdd () {
    dispatch(asyncAdd())
  }
}), IndexComponent) // IndexComponent是你的组件
// 然后通过this.props去读取属性或者调用方法

// 第二种,如果你要使用函数式组件,那么可以使用useSelector和useDispatch去使用
const dispatch = useDispatch();
dispatch(add())
...

从上面老的写法中,我们可以看出,在声明、使用redux的过程中,存在着很多繁琐的步骤。比如你需要创建一个新的reducer,那么上面counter的写法,你都得来一遍,每一个步骤又臭又长,把一个读写的逻辑搞的太过于复杂,模版代码就如counter的reducer文件中一样。

  • 新写法
    需要依赖:
package.json 复制代码
...
"@reduxjs/toolkit": "^2.8.2",
...

光这一个库,就包含了很多快捷写法:

  • configureStore()
  • createReducer()
  • createAction()
  • createSlice()
  • combineSlices()
  • createAsyncThunk
    ...
    我这里只罗列了基本使用方法。从这个列表中的函数名就可以看出,@reduxjs/toolkit的模块化更清晰明确。

下面我们看看如何使用。

  1. 创建store
store.ts 复制代码
import { configureStore } from "@reduxjs/toolkit";
import counter from "./modules/counter";

export default configureStore({
  reducer: {
    counter: counter,
  },
});
  1. 创建一个reducer,就是上面的counter
counter.ts 复制代码
import { createSlice } from "@reduxjs/toolkit";

const counterReducer = createSlice({
  name: "Counter",
  initialState: {
    num: 0
  },
  reducers: {
    add: (state, action) => {
      state.num = state.num + 1;
    },
    minus: (state, action) => {
      state.num = state.num - 1;
    },
    asyncAdd: (state, action) => {
      setTimeout(() => {
        state.num = state.num + 1;
      }, 2000)
    },
  },
});

export const { add, minus, asyncAdd } = userReducer.actions;

export default counterReducer.reducer;
  1. 函数组件使用(不要再用component组件了)
index.tsx 复制代码
import {
  add,
} from "@/store/modules/counter";
...
const num = useSelector((state: any) => state.counter.num);
const dispatch = useDispatch();
const handleAddAction = () => dispatch(add())
...

到此结束。

通过上面的新老写法对比,新写法的简单几步就可以将以前复杂的action、state简化到一个文件中,并且功能清晰,我也相信你已经对@reduxjs/toolkit有了进一步了解,所以尽可能使用新的写法去简化我们的代码吧。

暂无评论,快来发表第一条评论吧