《四》Redux Toolkit

Redux Toolkit(RTK)是 Redux 官方推荐的编写 Redux 逻辑的方法。Redux 的编写逻辑过于繁琐,并且代码通常分拆在多个文件中,Redux Toolkit 就旨在解决 Redux 编写代码过于繁琐和分散的问题。

Redux Toolkit 其实就是对 Redux 做了一层封装。

安装:

npm install @reduxjs/toolkit

直接安装 Redux Toolkit 即可,不需要再单独安装 Redux。
但是如果想要更好地与 React 结合使用,还是需要安装 react-redux

核心 API:

configureStore()

configureStore():创建 Store。用于替代 Redux 的 createStore() 方法。

接收一个对象作为参数,包含以下内容:

  1. reducer:对象类型。可以接收多个 reducer 函数。

    原有的 combineReducers() 合并函数就不需要了。

  2. middleware:对象类型。可以接收多个中间件。

    默认已经集成了 redux-thunkredux-devtool 中间件。

  3. devTools:布尔值类型。是否开启 devTools 工具,默认为 true。
// 引入 configureStore 方法
import {configureStore} from '@reduxjs/toolkit'
// 引入 reducer
import countReducer from './features/count'

// 创建 store
const store = configureStore({
  // 将导出的 slice 中的 reducer 传入
  reducer: {
    count: countReducer,
  },
})

// 导出 store
export default store

createSlice()

createSlice():创建一个 slice 片段。一个 slice 片段其实是 Action 和 Reducer 的集合。

createSlice() 接收一个对象作为参数,包含以下内容:

  1. name:字符串类型。标记 slice。

    作为 action.type 的前缀,在使用的时候默认使用 name/actionName,解决了 Action 中的 type 可能会同名的情况。
    在 Redux DevTools 调试工具中可以看到。

  2. initialState:对象类型。State 的初始值。

  3. reducers:对象类型,相当于之前的 Reducer 函数。

    对象中可以添加任意多个函数,每一个函数就相当于之前 Reducer 函数中的一个 case 语句。会接收到 State 和 Action 作为其参数(Action 有两个属性:type 和 payload,dispatch 派发 Action 时传入的参数就会作为 payload),直接修改 State 中对应的值即可。

    Redux Toolkit 底层使用了 immerjs 库,因此可以直接使用赋值的方式改变 State,不再需要每一次都返回一个新的 State。
    immerjs 底层是使用了 proxy 来对原状态进行了拦截,生成了草稿状态,在草稿状态上进行读写操作后,返回最终的状态。也就实现了既保证了原状态的不可变性,也保证了返回的是一个不同引用地址的新状态。

  4. extraReducers:对象类型或者函数类型。可以让 slice 处理在别处定义的 Action, 包括由 createAsyncThunk() 或其他 slice 生成的 Action。

createSlice() 的返回值是一个对象,包含以下属性:

  1. reducer:当前 slice 片段中的所有 Reducer。
  2. actions:当前 slice 片段中的所有 Action。
// 引入 createSlice
import {createSlice} from "@reduxjs/toolkit";

// 创建 slice。每一个 slice 里都包含了 reducer 和 actions,可以实现模块化的封装
const slice = createSlice({
  // 标记 slice,作为 action.type 的前缀
  name: 'count',
  // state 的初始值
  initialState: {
    count: 0,
  },
  // 相当于之前的 reducer 函数
  reducers: {
    // 一个函数就相当于之前的 reducer 函数中的一个 case 语句
    // 相当于既定义了组件中 dispatch 使用的同步的 Action(函数名就相当于 Action 的类型),也定义了 Reducer 更新状态函数(函数体就相当于 Reducer 更新 State)
    increaseCount(state, action) {
      // 可以直接使用赋值的方式修改 State,不再需要每一次都返回一个新的 State
      state.count += action.payload
    },
    decreaseCount(state, action) {
      state.count -= action.payload
    },
  }
})

// 导出 slice 片段中的 reducer,以供创建 store 时使用
export default slice.reducer

// 导出 slice 片段中的 action,以供组件中 dispatch 时使用
export const {increaseCount, decreaseCount} = slice.actions

createAsyncThunk()

createAsyncThunk():创建一个异步 Action。

接收两个参数:第一个参数是一个表示类型的字符串,用于标识这个异步 Action,在 Redux DevTools 调试工具中可以看到;第二个参数是一个函数,可以在其中进行异步操作,返回的数据会被传入到 createSlice() 的 extraReducers 中函数的 action.payload 参数中。

createAsyncThunk() 方法触发的时候会有三种状态:pending(进行中)、fulfilled(成功)、rejected(失败),可以在 createSlice() 的 extraReducers 中监听结果。

// 引入 createAsyncThunk
import {createSlice, createAsyncThunk} from "@reduxjs/toolkit";
import axios from "axios";

// 通过 createAsyncThunk 创建一个异步的 Action 并导出
export const getAsyncCountAction = createAsyncThunk('getBannerAction', async () => {
  // 执行异步操作
  const res = await axios.get('http://123.207.32.32:8000/home/multidata')
  // 返回的数据会被传入到 createSlice 的 extraReducers 中函数的 action.payload 参数中
  return res.data.data.banner.list.length
})

const slice = createSlice({
  name: 'count',
  initialState: {
    count: 0,
  },
  // extraReducers 可以让 slice 处理在别处定义的 Action,包括由 createAsyncThunk 创建的异步的 Action 或其他 slice 生成的 Action
  extraReducers: {
    [getAsyncCountAction.pending](state, action) {
      console.log('getAsyncCountAction.pending')
    },
    // 监听 createAsyncThunk 创建的异步 Action 的结果,修改 State 中指定的数据
    [getAsyncCountAction.fulfilled](state, action) {
      console.log('getAsyncCountAction.fulfilled')
      state.count = action.payload
    },
    [getAsyncCountAction.rejected](state, action) {
      console.log('getAsyncCountAction.rejected')
    },
  },
  // extraReducers 还有另外一种写法,可以是一个函数,接收一个 builder 作为参数,可以给 builder 添加 case 来监听异步操作的结果
  extraReducers: builder => {
    builder
      .addCase(getAsyncCountAction.pending, (state, action) => {
        console.log('getAsyncCountAction.pending')
      })
      .addCase(getAsyncCountAction.fulfilled, (state, action) => {
        console.log('getAsyncCountAction.fulfilled')
        state.count = action.payload
      })
  },
})

export default slice.reducer

export const {increaseCount, decreaseCount} = slice.actions
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
// 引入 Action
import {increaseCount, getAsyncCountAction} from '../store/features/count'
 
class Increase extends PureComponent {
  componentDidMount() {
    this.props.getInitialCount()
  }

  render() {
    return (
      <div>
        <h1>{this.props.count}</h1>
      </div>
    )
  }
}

const mapStateToProps = state => ({
  // 获取 store 中的 count 数据
  count: state.count.count,
})
const mapDispatchToProps = dispatch => ({
  // 初始获取从服务器中请求到的 count 数据
  getInitialCount: () => {
    dispatch(getAsyncCountAction())
  },
})
export default connect(mapStateToProps, mapDispatchToProps)(Increase)

示例:

请添加图片描述

// index.js
import ReactDOM from 'react-dom/client'
// 从 react-redux 中引入 Provider
import {Provider} from 'react-redux'
// 引入 store
import store from './store'

import App from './App'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  // 使用 <Provider> 包裹根组件,将 Store 作为属性赋值给 <Provider>
  <Provider store={store}>
    <App />
  </Provider>
)
// APP.jsx
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
// 引入 Action
import {increaseCount, getAsyncCountAction} from './store/features/count'
 
class APP extends PureComponent {
  componentDidMount() {
    this.props.getInitialCount()
  }

  handleIncrease = () => {
    this.props.handleCountIncrease()
  }

  render() {
    return (
      <div>
        <h1>{this.props.count}</h1>
        <button onClick={this.handleIncrease}>增加</button>
      </div>
    )
  }
}

const mapStateToProps = state => ({
  // 获取 store 中的 count 数据
  count: state.count.count,
})
const mapDispatchToProps = dispatch => ({
  // 初始获取从服务器中请求到的 count 数据
  getInitialCount: () => {
    dispatch(getAsyncCountAction())
  },
  handleCountIncrease: () => {
    // 修改 store 中的 count 数据
    dispatch(increaseCount(5))
  }
})
export default connect(mapStateToProps, mapDispatchToProps)(APP)
// store/index.js
// 引入 configureStore 方法
import {configureStore} from '@reduxjs/toolkit'
// 引入 reducer
import countReducer from './features/count'

// 创建 store
const store = configureStore({
  // 将导出的 slice 中的 reducer 传入
  reducer: {
    count: countReducer,
  },
})

// 导出 store
export default store
// store/feature/count.js
// 引入 createSlice
import {createSlice, createAsyncThunk} from "@reduxjs/toolkit";
import axios from "axios";

// 通过 createAsyncThunk 创建一个异步的 Action 并导出
export const getAsyncCountAction = createAsyncThunk('getBannerAction', async () => {
  // 执行异步操作
  const res = await axios.get('http://123.207.32.32:8000/home/multidata')
  // 返回的数据会被传入到 extraReducers 中函数的 action.payload 参数中
  return res.data.data.banner.list.length
})

// 创建 slice。每一个 slice 里都包含了 reducer 和 actions,可以实现模块化的封装
const slice = createSlice({
  // 标记 slice,作为 action.type 的前缀
  name: 'count',
  // state 的初始值
  initialState: {
    count: 0,
  },
  // 相当于之前的 reducer 函数
  reducers: {
    // 一个函数就相当于之前的 reducer 函数中的一个 case 语句
    // 相当于既定义了组件中 dispatch 使用的同步 Action(函数名就相当于 Action 的类型),也定义了 Reducer 更新状态函数(函数体就相当于 Reducer 更新 State)
    increaseCount(state, action) {
      // 可以直接使用赋值的方式修改 State,不再需要每一次都返回一个新的 State
      state.count += action.payload
    },
    decreaseCount(state, action) {
      state.count -= action.payload
    },
  },
  // extraReducers 可以让 slice 处理在别处定义的 Action,包括由 createAsyncThunk 创建的异步的 Action或其他 slice 生成的 Action
  extraReducers: {
    // 监听 createAsyncThunk 创建的异步 Action 的结果,修改 State 中指定的数据
    [getAsyncCountAction.fulfilled](state, action) {
      console.log('getAsyncCountAction.fulfilled')
      state.count = action.payload
    },
  },
})

// 导出 slice 片段中的 reducer,以供创建 store 时使用
export default slice.reducer

// 导出 slice 片段中的 action,以供组件中 dispatch 时使用
export const {increaseCount, decreaseCount} = slice.actions
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值