对项目中管理页面的高频操作搜索栏的思考与总结,对搜索栏搜索逻辑的hook化处理

背景与目的

项目是一套react的售卖/管理系统,有多个类目,基本布局相似,都有类似的搜索栏。不同类目的搜索逻辑有高度重合部分。考虑将该部分逻辑hook化,简化代码,降低开发难度,提高可读性以及后期可维护性。

功能设计

既然考虑将其hook化了,就不要只考虑将逻辑抽离,可以适当增加其功能,提升易用性。于是有以下考虑。

  1. 可以将一部分参数响应到url上,进入页面时可以将url参数响应到数据中心
  2. 有了1的考虑,再加上这种场景通常是与表格搭配使用。于是再考虑将业务逻辑中的查询搜索 、甚至是删除 操作,以及搜索条件都统一放到Model(数据层)中进行管理,比如redux。
  3. 由于将所有搜索条件都统一放到了Model(数据层)中管理,于是只需要监听该searchValues的变化然后响应搜索即可。根据其单向数据流的思想,用户在界面上的搜索操作,产生的搜索数据变化只需要流向两处即可,即url的search与Model(数据层)中searchValues。

功能交互逻辑如下图

方案实施

1. hook的参数定义

首先要有搜索条件 searchValues 是毋庸置疑的。然后,由于我们需要将用户的search操作劫持到hook中,将他产生的数据变化流向两个不同的地方,于是我们需要入参一个更改搜索条件的函数changeSearchValues 。再由功能设计中第一点的考虑,我们需要入参一个用户不可见参数列表invisiableSearchs 。最后,虽然我们是劫持了search操作,但是不同业务上的搜索逻辑可能略有不同,为了提高hook的泛用性,这里还需要入参具体的搜索函数onSearch。于是,具体的参数props定义可如下所示。

ts 复制代码
export interface BaseSearchValues {
  [key: string]: string | number | boolean | undefined | null;
}

export interface useQuerySearchProps<T extends BaseSearchValues = any> {
  searchValues: T;
  changeSearchValues: (values?: T) => void;
  invisiableSearchs?: string[];
  onSearch: (values?: T) => void;
}
2. 具体实施

实施过程中,考虑到还有刷新,多条件重复响应等。hook完整逻辑如下

ts 复制代码
import { useDebounceEffect } from 'ahooks';
import { isNull, omit, pickBy } from 'lodash';
import { useCallback, useEffect, useState } from 'react';
import { URLSearchParamsInit, useSearchParams } from 'react-router-dom';

export interface BaseSearchValues {
  [key: string]: string | number | boolean | undefined | null;
}

export interface useQuerySearchProps<T extends BaseSearchValues = any> {
  searchValues: T;
  changeSearchValues: (values?: T) => void;
  invisiableSearchs?: string[];
  onSearch: (values?: T) => void;
}

export const useQuerySearch = <T extends BaseSearchValues = {}>({
  searchValues,
  changeSearchValues,
  invisiableSearchs = [],
  onSearch,
}: useQuerySearchProps<T>) => {
  const [refreshTag, setRefreshTag] = useState(0);
  const [search, update] = useSearchParams();
  const urlSearchValues = search.values();

  const onRefresh = useCallback(() => {
    setRefreshTag(refreshTag + 1);
  }, [refreshTag, setRefreshTag]);

  /**
   * 搜索条件变化时调用此函数,只需要传入变化的参数即可。
   * 当搜索值为null时,表示删除此参数
   */
  const onSearchInputChange = useCallback((inputValues: T) => {
    const fullSearchValues = pickBy(
      { ...searchValues, ...inputValues } as T,
      (val: T[keyof T], _key: keyof T) => !isNull(val)
    ) as unknown as T;
    const visiableInputSearchs = omit(fullSearchValues, invisiableSearchs) as URLSearchParamsInit;

    /**
     * 用户可见参数响应到url
     */
    update(visiableInputSearchs);
    /**
     * 全量参数响应到model数据层
     */
    changeSearchValues(fullSearchValues);
  }, [urlSearchValues, searchValues, invisiableSearchs, update]);

  /**
   * 初始化参数
   */
  useEffect(() => {
    changeSearchValues({ ...searchValues, ...urlSearchValues } as T);
  }, []);

  /**
   * searchvalues变化,响应搜索action 同时需要防抖
   */
  useDebounceEffect(
    () => {
      /**
       * search功能,调用前可能还会做其他操作,这里将查询能力留给外部,只提供emit能力。
       */
      onSearch(searchValues);
    },
    [refreshTag, searchValues],
    { wait: 100 }
  );

  return {
    onRefresh,
    onSearchInputChange,
  };
};

总结

这里是以react为例的代码。但重要的不是代码本身,而是思想上的转变。不论是vue还是angular,又或者是其他框架,都可以以此为基础去处理。在hook化之前书写的时候看代码可能没有相似度,但具体逻辑基本如此,如此高重复度且大量的代码,在hook化之后只需要寥寥几行代码即可完成。

相关推荐
Irene199143 分钟前
ElementPlus 与成熟后台框架对比:vue-element-plus-admin、vue-pure-admin等
前端·ui·框架·vue3
尘中客5 小时前
放弃 Echarts?前端直接渲染后端高精度 SVG 矢量图流的踩坑记录
前端·javascript·echarts·前端开发·svg矢量图·echarts避坑
FreeBuf_5 小时前
Chrome 0Day漏洞遭野外利用
前端·chrome
小彭努力中5 小时前
199.Vue3 + OpenLayers 实现:点击 / 拖动地图播放音频
前端·vue.js·音视频·openlayers·animate
2501_916007475 小时前
网站爬虫原理,基于浏览器点击行为还原可接口请求
前端·javascript·爬虫·ios·小程序·uni-app·iphone
前端大波6 小时前
Sentry 每日错误巡检自动化:设计思路与上手实战
前端·自动化·sentry
ZC跨境爬虫7 小时前
使用Claude Code开发校园交友平台前端UI全记录(含架构、坑点、登录逻辑及算法)
前端·ui·架构
慧一居士7 小时前
Vue项目中,何时使用布局、子组件嵌套、插槽 对应的使用场景,和完整的使用示例
前端·vue.js
Можно7 小时前
uni.request 和 axios 的区别?前端请求库全面对比
前端·uni-app
M ? A7 小时前
解决 VuReact 中 ESLint 规则冲突的完整指南
前端·react.js·前端框架