Taro跨端开发探索15——集成reduxjs/toolkit,开发状态管理器actions

853 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情

前言

昨天我们实现了商品列表页面价格和销量的筛选条件开发。今天我们探索一下如何完成复杂的筛选条件开发。

功能分析

我们可以看一下京喜小程序的筛选页面
1650549000(1).png
我们可以看到,在点击筛选按钮区域的时候在右侧弹出来一个抽屉组件。在这个组件中可以进一步的精确搜索。所以我们要实现的就是在两个组件中进行数据传输

功能实现

我们实现的方法有很多种。包括但不限于以下几种方式

  • props传参
  • 调用组件中的方法互相传参
  • 使用状态管理器 其实看过我之前关于父子组件之间传值文章的读者对props传参比较熟悉了。但是随着业务的发展,我们的props变得越来越臃肿。此时我们就要使用react的状态管理器中的action来解决这个问题。
    熟悉vue开发的读者必然离不开的话题就是状态管理器vue,今天我们就探索一下react中的状态管理器:redux-toolkit的使用

redux-toolkit使用

其实一开始我是想使用redux-actions来管理状态的,但是在import { createStore } from 'redux';的时候,编译器提示createStore已过时,让我们使用redux-toolkit中的初始化方法,于是我就在网上找到了这篇文章.redux最佳实践redux-toolkit
我们根据上边的文章的步骤来编写我们自己的状态管理器

引入依赖

在项目下执行命令npm i @reduxjs/toolkit

定义我们自己的状态数据

我们将状态数据作为对象格式化后传参。把定义action的所有方法放在一个地方。

在src目录下创建store/features文件夹。在这个文件夹下创建goodsQueryFilterSlice.ts,内容如下

import { createSlice } from "@reduxjs/toolkit";
export const goodsQueryFilterSlice = createSlice({
  // 命名空间,在调用action的时候会默认的设置为action的前缀
  name: "goodsQueryFilter",
  // 初始值
  initialState: {
    filterData: {},
  },
  // 这里的属性会自动的导出为actions,在组件中可以直接通过dispatch进行触发
  reducers: {
    updateFilter(state, { payload }) {
      // 内置了immutable
      state.filterData = payload;
    },
  },
});
// 导出actions
export const { updateFilter } = goodsQueryFilterSlice.actions;
// 导出reducer,在创建store时使用到
export default goodsQueryFilterSlice.reducer;

待会在使用的地方我会标注一下对应的定义,大家向下看就可以了

定义store 在store下新建index.ts,内容如下

import { configureStore } from "@reduxjs/toolkit";
import goodsQueryFilterSlice from "./features/goodsQueryFilterSlice";
// configureStore创建一个redux数据
export default configureStore({
  reducer: {
    goodsQueryFilter: goodsQueryFilterSlice,
  },
});

我们找了就是简单的定义了一个状态管理器,里面有goodsQueryFilter这个状态

修改项目app.ts

我们找到项目的src下的app.ts,将它修改为app.tsx,内容如下

import { Component } from "react";
import "./app.scss";
import { Provider } from "react-redux";
import store from "./store";

class App extends Component {
  componentDidMount() {}

  componentDidShow() {}

  componentDidHide() {}

  componentDidCatchError() {}

  // this.props.children 是将要会渲染的页面
  render() {
    return <Provider store={store}>{this.props.children}</Provider>;
  }
}

export default App;

这里的作用和vue中的app.user(store)效果差不多,<Provider store={store}>引入了我们的store

定义筛选的抽屉组件

因为打开的抽屉组件里的筛选条件比较多,且逻辑比较复杂,我们将它封装为一个单独的组件,代码如下
sub-query-pages/goods-list/goods-filter-drawer/index.scss引入scss

@import "~taro-ui/dist/style/components/drawer.scss";
@import "~taro-ui/dist/style/components/input.scss";

sub-query-pages/goods-list/goods-filter-drawer/index.tsx开发组件

import { View } from "@tarojs/components";
import { useState } from "react";
import { AtButton, AtDrawer, AtInput } from "taro-ui";
import { updateFilter } from "../../../store/features/goodsQueryFilterSlice";
import { useDispatch } from "react-redux";
import "./index.scss";
export default function GoodsFilterDrawer(props: any) {
  const [minPrice, setMinPrice] = useState("" as any);
  const [maxPrice, setMaxPrice] = useState("" as any);
  const dispatch = useDispatch();
  return (
    <View>
      <AtDrawer
        show={props.drawerShow}
        right
        mask
        onClose={() => {
          props.closeDrawer();
        }}
      >
        <View>
          <AtInput
            title="最低价"
            placeholder="请输入最低价"
            name="minPrice"
            type="number"
            value={minPrice}
            onChange={(value) => {
              setMinPrice(value);
            }}
          ></AtInput>
          <AtInput
            title="最高价"
            placeholder="请输入最高价"
            name="maxPrice"
            type="number"
            value={maxPrice}
            onChange={(value) => {
              setMaxPrice(value);
            }}
          ></AtInput>
          <AtButton
            type="primary"
            onClick={() => {
              dispatch(updateFilter({ minPrice, maxPrice }));
              props.closeDrawer();
            }}
          >
            筛选
          </AtButton>
        </View>
      </AtDrawer>
    </View>
  );
}

1650637417(1).png

关于minPrice和maxPrice的useState我就不赘述了。在上边这个截图中,我框处理的地方从上到下依次的作用为

  • 使用组件传参控制抽屉显示
  • 抽屉关闭必须的时候设置控制状态的属性为false,否则抽屉无法再次打开
  • 使用dispatch调用action
  • action的引用为
import { updateFilter } from "../../../store/features/goodsQueryFilterSlice";

1650637838(1).png

大家可以看一下我画的两个箭头

  • 红色箭头代表【调用流程】:
    1. 我调用了goodsQueryFilterSlice里的updateFilter action
    2. 这个action是reducers自动导出的
  • 蓝色箭头代表【传参】。即我传入了{minPrice:minPrice,maxPrice:maxPrice}至action中

最后看看商品列表如何调用状态

sub-query-pages/goods-list/index.tsx代码如下

import { View } from "@tarojs/components";
import { useState } from "react";
import { AtButton, AtDrawer, AtInput } from "taro-ui";
import { updateFilter } from "../../../store/features/goodsQueryFilterSlice";
import { useDispatch } from "react-redux";
import "./index.scss";
export default function GoodsFilterDrawer(props: any) {
  const [minPrice, setMinPrice] = useState("" as any);
  const [maxPrice, setMaxPrice] = useState("" as any);
  const dispatch = useDispatch();
  return (
    <View>
      <AtDrawer
        show={props.drawerShow}
        right
        mask
        onClose={() => {
          props.closeDrawer();
        }}
      >
        <View>
          <AtInput
            title="最低价"
            placeholder="请输入最低价"
            name="minPrice"
            type="number"
            value={minPrice}
            onChange={(value) => {
              setMinPrice(value);
            }}
          ></AtInput>
          <AtInput
            title="最高价"
            placeholder="请输入最高价"
            name="maxPrice"
            type="number"
            value={maxPrice}
            onChange={(value) => {
              setMaxPrice(value);
            }}
          ></AtInput>
          <AtButton
            type="primary"
            onClick={() => {
              dispatch(updateFilter({ minPrice, maxPrice }));
              props.closeDrawer();
            }}
          >
            筛选
          </AtButton>
        </View>
      </AtDrawer>
    </View>
  );
}

这里其实就一行代码和状态管理器有关

const { filterData } = useSelector((state: any) => state.goodsQueryFilter);

useSelector:从状态管理器中取出状态,goodsQueryFilter对应我们action的name space

结果验证

最后我们在输入筛选条件之后,点击搜素时,控制台会输出我们的filterData,效果如下

1650638649(1).png

结语

在探索action的时候,我看到了有的项目使用的是dva。最后没有使用,不可否认dva用作状态管理器的优势。
但是假如说我们要引入dva的话。会引起两个问题

  • 比起本文介绍的tookiit编码来说,dva稍显繁琐
  • 引入dva之后,打包的dist文件变大
    最后,欢迎大家多多关注本系列文章。本文所有代码均已上gitee,地址:gitee.com/liangminghu…