联动模糊搜索

背景

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

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

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

实现思路

  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;
}

部分效果预览

搜索中:

总结:

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

使用缓存减少性能消耗

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

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

相关推荐
liuchangng20 小时前
React 19 + Vite 6 实战:从教材 PDF 到 866 词间隔重复单词 App
前端·react.js·pdf·vite·开源项目
鸿蒙开发1 天前
Three.js 首帧卡顿排查:compileAsync 之后,为什么还会停一下?
react.js·three.js
小李科技2 天前
【性能优化】事件委托:目标定位与 React/Vue 实战
前端·vue.js·react.js·面试·性能优化·前端框架
csj502 天前
前端基础之《React(16)—Modal组件封装》
前端·react.js
汤米粥3 天前
目前流行的前端 CSS 框架
前端·css·bootstrap·ant design·‌tailwind css·bulma
ZoeLandia4 天前
React11:数据请求与 TanStack Query
前端·react.js·typescript·react
瓦瓦gogo4 天前
实习中接触前端开发后,我开始了解 React
前端·react.js·前端框架
ZoeLandia5 天前
React08:自定义 Hook
开发语言·前端·react.js·typescript
空中湖6 天前
用 React 写数据大屏:为什么我不建议用图表库
前端·react.js·前端框架
来一首_We_go8 天前
React Flow Pro 高级交互实现
前端·react.js