业务背景
在日复一日的开发中,产品突然找我,说:"鱼儿,我想下拉框选个数据,我怎么找不到我想选的选项啊?
"。我看了一眼产品,当及翻了一个白眼:"哥,你眼神不好吧
"。产品看我眼神不对,没有说话,对我甩出一张图,其效果果大概类似这样:
数据有几千条,用户需要从几千条里选出自己想要的选项
,我看了看,好家伙,这这这,百步穿杨的养由基怕也得眼神不好啊,于是我默默低下了头,败下阵来:"哥,我优化一下交互"。
解决思路
那怎么解决这个问题呢?先找痛点!!
这个问题的痛点
是用户如何从海量的数据中找出自己想要的选项
。
那用户想要的选项是啥呢?开发是不知道的,只有用户自己知道,且每个用户想要的选项是个性化的,开发不能做限制。
那开发能做什么呢?
我想能做的就是提供搜索能力,让用户从原先的肉眼搜索变为机器搜索
登场角色
好了,痛点已经出来了,那我们结合当前项目已有的技术栈(react+ts+vite+antd+loadsh),确定需要登场的角色:
antd的select组件、一双勤劳的手
原有代码
js
/**
* 模糊搜索demo
*/
import React, { useEffect, useState, useCallback } from 'react';
import { Select } from 'antd';
import { getfuzzySearchData } from './service/index'
const FuzzySearch = () => {
const [options, setOptions] = useState<any[]>([]);
const fuzzySearchDataApi = async (params?: any) => {
const res = await getfuzzySearchData(params);
const { data } = res;
setOptions(data);
}
useEffect(() => {
fuzzySearchDataApi();
}, [])
return (
<Select
style={{ width: 120 }}
options={options}
/>
)
}
export default FuzzySearch;
具体效果如图:
解决步骤
1、添加搜索能力
利用antd中select的现有能力showSearch
添加上就可以搜索了:
代码如下:
js
/**
* 模糊搜索demo
*/
import React, { useEffect, useState, useCallback } from 'react';
import { Select } from 'antd';
import { getfuzzySearchData } from './service/index'
const FuzzySearch = () => {
const [options, setOptions] = useState<any[]>([]);
const fuzzySearchDataApi = async (params?: any) => {
const res = await getfuzzySearchData(params);
const { data } = res;
setOptions(data);
}
useEffect(() => {
fuzzySearchDataApi();
}, [])
return (
<Select
showSearch // 增加了这个
style={{ width: 120 }}
options={options}
/>
)
}
export default FuzzySearch;
2、模糊搜索
利用已有的filterOption
效果如图:
代码如下:
js
/**
* 模糊搜索demo
*/
import React, { useEffect, useState, useCallback } from 'react';
import { Select } from 'antd';
import { getfuzzySearchData } from './service/index'
const FuzzySearch = () => {
const [options, setOptions] = useState<any[]>([]);
const fuzzySearchDataApi = async (params?: any) => {
const res = await getfuzzySearchData(params);
const { data } = res;
setOptions(data);
}
// 新增代码,用于筛选需要展示的数据
const filterOption = (input: string, option: { label: string; value: string }) => {
return (option?.label ?? '').toLowerCase().includes(input.toLowerCase());
}
useEffect(() => {
fuzzySearchDataApi();
}, [])
return (
<Select
showSearch
style={{ width: 120 }}
options={options}
filterOption={filterOption} // 新增代码
/>
)
}
export default FuzzySearch;
上面已经基本解决了模糊搜索的问题,这时候后端老师找我:"鱼儿,产品过来找我,说这个接口太慢了,让我优化一下,我之后不一次性返回所有数据了哈,你给我传个参数,我根据参数一次返回100条吧
"。我说好好好,年轻人不讲武德,欺负我一个小前端是吧,我改!!!!!!
3、实时获取数据
上面已经基本解决了模糊搜索的问题,但是又有一个问题,当数据量过于多的时候,后端考虑到接口的速度、性能,可能会做出这样的决策:不会一下子返回几千条数据,只会根据对应的参数返回少量的数据
。
代码如下:
js
/**
* 模糊搜索demo
*/
import React, { useEffect, useState, useCallback } from 'react';
import { Select } from 'antd';
import { debounce } from 'loadsh'; // 新增代码
import { getfuzzySearchData } from './service/index'
const FuzzySearch = () => {
const [options, setOptions] = useState<any[]>([]);
const fuzzySearchDataApi = async (params?: any) => {
const res = await getfuzzySearchData(params);
const { data } = res;
setOptions(data);
}
const filterOption = (input: string, option: { label: string; value: string }) => {
return (option?.label ?? '').toLowerCase().includes(input.toLowerCase());
}
// 新增代码,用于获取实时数据,使用useCallback防止函数被重复创建,使用debounce用于减少用户请求的次数,进行优化
const getActualTime = useCallback(debounce(async (value: string) => {
const params = {
name: value
}
fuzzySearchDataApi(params);
}, 1000), []);
useEffect(() => {
fuzzySearchDataApi();
}, [])
return (
<Select
showSearch
style={{ width: 120 }}
options={options}
filterOption={filterOption}
onSearch={(value: string) => { // 使用onSearch,当文本框值变化时调用
getActualTime(value);
}}
/>
)
}
export default FuzzySearch;
效果如图:
4、踩坑
美滋滋的解决模糊搜索的问题后,我看了此页面的另一个有着大数据的select框,决定也浅浅优化一下吧。
优化后代码如下:
js
/**
* 模糊搜索demo
*/
import React, { useEffect, useState, useCallback } from 'react';
import { Select } from 'antd';
import { debounce } from 'loadsh';
import { getfuzzySearchData } from './service/index'
const FuzzySearch = () => {
const [options, setOptions] = useState<any[]>([]);
const fuzzySearchDataApi = async (params?: any) => {
const res = await getfuzzySearchData(params);
const { data } = res;
setOptions(data);
}
const filterOption = (input: string, option: { label: string; value: string }) => {
return (option?.label ?? '').toLowerCase().includes(input.toLowerCase());
}
const getActualTime = useCallback(debounce(async (value: string) => {
const params = {
name: value
}
fuzzySearchDataApi(params);
}, 1000), []);
useEffect(() => {
fuzzySearchDataApi();
}, [])
return (
<Select
showSearch
style={{ width: 120 }}
options={options}
filterOption={filterOption}
fieldNames={{ label: 'name', value: 'code' }}
onSearch={(value: string) => {
getActualTime(value);
}}
/>
)
}
export default FuzzySearch;
但是此时,下拉框并没有达到我想要的效果,此时的问题是,当我在搜索框搜索对应的数据的时候,明明下拉框的数据里有该数据,却显示无数据
效果如图:
经过反复对比,我终于发现,问题的关键在于:返回的数据格式
,两个数据格式对比:
由此,我们也得到一个antd的机制:fieldNames与filterOption处理的数据都是原始数据的副本,互不影响
。虽然我们已经使用fieldNames={{ label: 'name', value: 'code' }}
进行了处理,但是filterOption接收的是原始数据
,并不是经过fieldNames
处理过的数据。
得到这个结论,那我们的处理思路也就很简单了,就是在接收到原始数据进行转化或者直接更改filterOption函数
1、在接收到原始数据进行转化
js
/**
* 模糊搜索demo
*/
import React, { useEffect, useState, useCallback } from 'react';
import { Select } from 'antd';
import { debounce } from 'loadsh';
import { getfuzzySearchData } from './service/index'
const FuzzySearch = () => {
const [options, setOptions] = useState<any[]>([]);
// 进行数据处理
const handleToCodeName = (data: any[]) => {
const result = [];
data.forEach((item,index) => {
result.push({
label: item.name,
value: item.code
})
})
return result;
}
const fuzzySearchDataApi = async (params?: any) => {
const res = await getfuzzySearchData(params);
const { data } = res;
setOptions(handleToCodeName(data)); // 在这里进行数据处理函数的调用
}
const filterOption = (input: string, option: { label: string; value: string }) => {
return (option?.label ?? '').toLowerCase().includes(input.toLowerCase());
}
const getActualTime = useCallback(debounce(async (value: string) => {
const params = {
name: value
}
fuzzySearchDataApi(params);
}, 1000), []);
useEffect(() => {
fuzzySearchDataApi();
}, [])
return (
<Select
showSearch
style={{ width: 120 }}
options={options}
// fieldNames={{ label: 'name', value: 'code' }} // 此时不需要这个
filterOption={filterOption}
onSearch={(value: string) => {
getActualTime(value);
}}
/>
)
}
export default FuzzySearch;
2、更改filterOption函数:
js
/**
* 模糊搜索demo
*/
import React, { useEffect, useState, useCallback } from 'react';
import { Select } from 'antd';
import { debounce } from 'loadsh';
import { getfuzzySearchData } from './service/index'
const FuzzySearch = () => {
const [options, setOptions] = useState<any[]>([]);
const fuzzySearchDataApi = async (params?: any) => {
const res = await getfuzzySearchData(params);
const { data } = res;
setOptions(data);
}
// 将label改为name,value改为code
const filterOption = (input: string, option: { name: string; code: string }) => {
return (option?.name ?? '').toLowerCase().includes(input.toLowerCase());
}
const getActualTime = useCallback(debounce(async (value: string) => {
const params = {
name: value
}
fuzzySearchDataApi(params);
}, 1000), []);
useEffect(() => {
fuzzySearchDataApi();
}, [])
return (
<Select
showSearch
style={{ width: 120 }}
options={options}
fieldNames={{ label: 'name', value: 'code' }}
filterOption={filterOption}
onSearch={(value: string) => {
getActualTime(value);
}}
/>
)
}
export default FuzzySearch;
总结
以上已经解决了模糊搜索的问题了,产品验收过后,面带意味寻常的微笑离去了
。