联动模糊搜索

背景

需要从用户选中的机构中,联动加载相对应的有权处理人。

由于有权处理人数据量较大,并且需要动态加载,无法将所有枚举都放在前端静态资源中。

需要根据所选择的机构,默认加载一部分的处理人,剩下的,根据用户手输的数据,与后台交互,模糊匹配对应的选项。

实现思路

  1. 使用React + hook + antd

  2. 需要对搜索框进行防抖处理,避免性能浪费

  3. 用户清除搜索条件时,应该将枚举列表恢复为上一状态。

主要代码实现

DebounceSelect:动态加载的下拉框

jsx 复制代码
import React, { forwardRef, useImperativeHandle, useState, useMemo } from 'react';
import { Select, Spin } from "antd";
import debounce from "lodash/debounce";

export const DebounceSelect = forwardRef(({ fetchOptions, debounceTimeout = 800, ...props}, ref) => {
    const [fetching, setFetching] = useState(false);
    const [options, setOptions] = useState([]);
    const [userListBackUp, setUserListBackUp] = useSate([]);
    const [centerHandleOrg, setCenterHandleOrg] = useState("");
    const debounceFetcher = useMemo(() => {
        // keyWord:用户输入的关键字
        const loadOptions = (keyWord) => {
            if(!keyWord) return
            setOptions([]);
            setFetching(true);
            // 同时触发父组件的fetchUserList,并传入参数
            fetchOptions({centerHandleOrg, keyWord}).then(newOptions => {
                setOptions(newOptions);
                setFetching(false)
            })
        }
        return debounce(loadOptions, debounceTimeout);
    },[ centerHandleOrg, fetchOptions, debounceTimeout])
    
    // 向父组件抛出对象
    useImperativeHandle(ref, () => ({
        update: (list = [], _centerHandleOrg = "") => {
            setOptions(list);
            setUserListBackUp(list);
            setCenterHandleOrg(_centerHandleOrg);
        }
    }));
    
    return(
        <Select
            allowClear
            showSearch
            filterOption={false}
            onSearch={debounceFetcher}
            onClear={() => setOptions(userListBackUp)}
            notFoundContent={fetching ? <Spin size="small"/> : null}
            {...props}
            options={options}
        />
    )
})

父组件中机构和处理人的部分静态代码实现:

jsx 复制代码
// ...相关依赖引入
<Row>
	<Col span={11}>
    	<Form.Item label="处理机构" name="centerHandleOrg">
            <Select 
                allowClear
                showSearch
                filterOption={(val, option) => selectFilterIsBool(val, option)}
                onSelect={this.centerHandleOrgChangeHandle}
                onClear={() => this.debounceSelectRef.current.update()}>
            {this.state.centerHandleOrgList.map(item => (
                	<Option key={item.orgCode} value={item.orgCode}>
                    	{item.orgCode}-{item.orgName}
                    </Option>
                ))}
            
            </Select>     
        </Form.Item>
    </Col>
    <Col span={11}>
        <Form.Item label="处理人" name="centerHandleUser">
        	<DebounceSelect 
                ref={this.debounceSelectRef}
                placeholder="请输入进行查找"
                fetchOption={this.fetchUserList}
                />
        </Form.Item>
    </Col>
</Row>

部分关键业务逻辑代码实现

jsx 复制代码
// 当用户选中某一机构时
centerHandleOrgChangeHandle = async (v) => {
    // 先置空处理人栏位
    this.formRef.current.setFieldsValue({
        centerHandleUser: "",
    })
    if(!v){
        return
    }
    // 获取有权用户列表(非全量)
    const data = {centerHandleOrg: v};
    const {list = []} = await getOrgUserList(data);
    this.debounceSelectRef.current.update(reNameObj(list));
}

// 当用户输入关键字进行检索有权用户时
// 在子组件中会触发onSearch => 触发fetchUserList
fetchUserList = async (data) => {
    return new Promise(resolve => {
         const data = {centerHandleOrg: v};
         const {list = []} = await getOrgUserList(data);
         resolve(reNameObj(list)); // 向子中注入新的options,查询数据完成
    })
}

部分使用到的方法代码实现如下:

js 复制代码
// 由于select是默认按照label,value做键的
const reNameObj = (list) => 
	list.map(({userName, userCode, ...rest}) => ({
        label: `${userCode}-${userName},`
		value: userCode,
        ...rest,
    }))

// 前端进行模糊匹配的时候,如果返显的下拉选项为拼接后的话,会被拆成一个数组,无法直接匹配
const selectFilterIsBool = (val = "", option = "") => {
    if(option.props.children){
        if(Objec.prototype.toString.call(option.props.children) !== "[object Array]"){
            return option.props.children.includes(val);
        }
        return option.props.children.join("").includes(val);
    }
    return false;
}

部分效果预览

搜索中:

总结:

使用防抖减少接口频繁调用

使用缓存减少性能消耗

使用数据备份这种方式提示用户体验

仅作为工作记录,感谢大家观看

相关推荐
今天也想MK代码7 小时前
ReFormX:现代化的 React 表单解决方案 - 深度解析与最佳实践
前端·react.js·性能优化
yanyu-yaya7 小时前
第三章 react redux的学习之redux和react-redux,@reduxjs/toolkit依赖结合使用
javascript·学习·react.js
dleei7 小时前
react入门(上)
前端·react.js·前端框架
哟哟耶耶8 小时前
React-02初学hello_react(JSX,创建React根节点,引入对应React库,render渲染DOM)
前端·javascript·react.js
lbh8 小时前
React + PDF.js 实战:构建一个带打印/下载功能的 PDF 查看器
javascript·react.js
fightingles9 小时前
写给自己看的React注意事项
react.js
代码小学僧9 小时前
如何优雅地重构一个企业官网 Nextjs 前端项目
前端·react.js·前端工程化
三小河9 小时前
自定义VITE插件,生成可修改配置项,用于不同项目部署
前端·javascript·react.js
小钰能吃三碗饭14 小时前
第一篇:【前端翻身计划】从菜鸟到高手,JavaScript ES6+实战秘籍揭秘!
前端·javascript·react.js
蓉妹妹15 小时前
AntDesign下,Select内嵌Menu标签,做一个多选下拉框,既可以搜索,还可以选择下拉项
前端·react.js