一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情。
前言
昨天我们实现了商品列表页面价格和销量的筛选条件开发。今天我们探索一下如何完成复杂的筛选条件开发。
功能分析
我们可以看一下京喜小程序的筛选页面
我们可以看到,在点击筛选按钮区域的时候在右侧弹出来一个抽屉组件。在这个组件中可以进一步的精确搜索。所以我们要实现的就是在两个组件中进行数据传输
功能实现
我们实现的方法有很多种。包括但不限于以下几种方式
- 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>
);
}
关于minPrice和maxPrice的useState我就不赘述了。在上边这个截图中,我框处理的地方从上到下依次的作用为
- 使用组件传参控制抽屉显示
- 抽屉关闭必须的时候设置控制状态的属性为false,否则抽屉无法再次打开
- 使用dispatch调用action
- action的引用为
import { updateFilter } from "../../../store/features/goodsQueryFilterSlice";
大家可以看一下我画的两个箭头
- 红色箭头代表【调用流程】:
- 我调用了goodsQueryFilterSlice里的updateFilter action
- 这个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,效果如下
结语
在探索action的时候,我看到了有的项目使用的是dva。最后没有使用,不可否认dva用作状态管理器的优势。
但是假如说我们要引入dva的话。会引起两个问题
- 比起本文介绍的tookiit编码来说,dva稍显繁琐
- 引入dva之后,打包的dist文件变大
最后,欢迎大家多多关注本系列文章。本文所有代码均已上gitee,地址:gitee.com/liangminghu…