联动模糊搜索

背景

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

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

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

实现思路

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

部分效果预览

搜索中:

总结:

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

使用缓存减少性能消耗

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

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

相关推荐
空中海11 小时前
01 React Native 基础、核心组件与布局体系
javascript·react native·react.js
空中海12 小时前
05 React架构设计、项目实践与专家清单
前端·react.js·前端框架
空中海14 小时前
04 工程化、质量体系与 React 生态
前端·ubuntu·react.js
空中海14 小时前
03 性能、动画与 React Native 新架构
react native·react.js·架构
空中海16 小时前
02 React Native状态、导航、数据流与设备能力
javascript·react native·react.js
空中海17 小时前
04 React Native工程化、质量、发布与生态选型
javascript·react native·react.js
郑生zs19 小时前
Hooks-useEffect
react.js
光影少年19 小时前
react函数组件、类组件、纯组件、受控/非受控组件
前端·react.js·掘金·金石计划
空中海21 小时前
05 React Native架构设计、主线项目与专家实践
javascript·react native·react.js
killerbasd1 天前
还是迷茫 5.3
前端·react.js·前端框架