联动模糊搜索

背景

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

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

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

实现思路

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

部分效果预览

搜索中:

总结:

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

使用缓存减少性能消耗

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

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

相关推荐
新星_3 小时前
路由跳转和路由参数
react.js
老码沉思录5 小时前
React Native 全栈开发实战班 - 用户界面之手势系统应用
react native·react.js·ui
Jimmy7 小时前
React 开发 - 认识 Zustand
前端·javascript·react.js
老码沉思录8 小时前
React Native 全栈开发实战班 - 用户界面进阶之流行 UI 库使用与集成
react native·react.js·ui
新星_8 小时前
react-router的使用
react.js
小牛itbull9 小时前
ReactPress与WordPress:两大开源发布平台的对比与选择
javascript·react.js·开源·wordpress·reactpress
yqcoder9 小时前
react 中 useRef Hook 作用
前端·javascript·react.js
老码沉思录9 小时前
React Native 全栈开发实战班 - 用户界面进阶之自定义组件开发
react native·react.js·ui
西瓜watermelon9 小时前
React 元素条件渲染:解锁动态界面的关键密码
前端·react.js
老码沉思录13 小时前
React Native 全栈开发实战班 - 用户界面进阶之响应式设计实践
react native·react.js·ui