React+TS前台项目实战(二十五)-- 全局常用排序组件SortButton封装

文章目录

  • 前言
  • SortButton组件
    • 1. 功能分析
    • 2. 代码+详细注释
    • 3. 使用到的全局hook代码
    • 4. 使用方式
    • 5. 效果展示
  • 总结


前言

今天要封装的SortButton 组件,主要用在表格列排序上,运用于更新路由并跳转更新,起到刷新页面仍然处于当前排序数据。

SortButton组件

1. 功能分析

(1)用于显示一个可以点击的按钮,用于排序数据
(2)接受一个field参数,用于指定要排序的字段
(3)可选择性接收一个sortParam参数,用于传递排序参数,如果未提供,则使用useSortParam的默认结果
(4)点击按钮时会触发排序动作,由handleSortClick函数定义

2. 代码+详细注释

// @/components/SortButton/index.tsx
import { ReactComponent as SortIcon } from "../../assets/sort_icon.svg";
import { SortButtonContainer } from "./styled";
import { useSortParam } from "@/hooks";
/**
 * 排序按钮组件
 * @param field - 要排序的字段名
 * @param sortParam - 排序参数,可选,默认使用useSortParam的结果
 */
export function SortButton<T extends string>({ field, sortParam }: { field: T; sortParam?: ReturnType<typeof useSortParam<T>> }) {
  // 获取排序参数
  const sortParamByQuery = useSortParam();
  // 获取排序字段、排序方式和排序事件处理函数
  const { sortBy, orderBy, handleSortClick } = sortParam ?? sortParamByQuery;
  // 判断当前字段是否为激活状态
  const isActive = sortBy === field;

  // 点击按钮时的事件处理函数
  const handleClick = () => {
    handleSortClick(field);
  };

  return (
    // 渲染排序按钮
    <SortButtonContainer>
      <button type="button" data-order={isActive ? orderBy : null} onClick={handleClick}>
        <SortIcon />
      </button>
    </SortButtonContainer>
  );
}
export default SortButton;
------------------------------------------------------------------------------
// @/components/SortButton/styled.tsx
import styled from "styled-components";
export const SortButtonContainer = styled.div`
  appearance: none;
  border: none;
  outline: none;
  background: none;
  display: inline-flex;
  vertical-align: text-top;
  margin-left: 8px;
  cursor: pointer;

  &[data-order="desc"] {
    svg > path:first-child {
      fill: var(--cd-primary-color);
    }
  }

  &[data-order="asc"] {
    svg > path:last-child {
      fill: var(--cd-primary-color);
    }
  }
`;

3. 使用到的全局hook代码

import { useEffect, useState, useCallback, useMemo } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import { omit, omitNil } from "@/utils/object";

/**
 * @description 获取URL中的查询参数
 * @param search 查询参数字符串
 * @param names 需要获取的参数名称
 * @returns 参数对象
 */
function getSearchParams<T extends string = string>(search: string, names?: T[]): Partial<Record<T, string>> {
  const urlSearchParams = new URLSearchParams(search);
  const entries = [...urlSearchParams.entries()].filter((entry): entry is [T, string] => names == null || (names as string[]).includes(entry[0]));
  return Object.fromEntries(entries) as Partial<Record<T, string>>;
}

/**
 * @description 获取当前URL中的查询参数
 * @param names 需要获取的参数名称
 * @returns 参数对象
 */
export function useSearchParams<T extends string>(...names: T[]): Partial<Record<T, string>> {
  const location = useLocation();
  return useMemo(() => getSearchParams(location.search, names), [location.search, names]);
}

/**
 * @description 更新URL中的查询参数
 * @returns 更新后的URL
 */
export function useUpdateSearchParams<T extends string>(): (updater: (current: Partial<Record<T, string>>) => Partial<Record<T, string | null | undefined>>, replace?: boolean, routing?: boolean) => string {
  const navigate = useNavigate();
  const { search, pathname, hash } = useLocation();

  return useCallback(
    (updater, replace, routing = true) => {
      const oldParams: Partial<Record<T, string>> = getSearchParams(search);
      const newParams = omitNil(updater(oldParams));
      const newUrlSearchParams = new URLSearchParams(newParams as Record<string, string>);
      newUrlSearchParams.sort();
      const newQueryString = newUrlSearchParams.toString();
      const to = `${pathname}${newQueryString ? `?${newQueryString}` : ""}${hash}`;

      if (routing) {
        if (replace) {
          navigate.replace(to);
        } else {
          navigate.push(to);
        }
      }

      return to;
    },
    [hash, navigate, pathname, search]
  );
}

/**
 * @description 排序参数
 * @type SortType 排序字段类型
 */
export type OrderByType = "asc" | "desc";

/**
 * @description 获取排序参数
 * @param isSortBy 判断是否为排序字段的函数
 * @param defaultValue 默认排序字段
 * @returns 排序参数对象
 */
export function useSortParam<T extends string>(
  isSortBy?: (s?: string) => boolean,
  defaultValue?: string
): {
  sortBy: T | undefined;
  orderBy: OrderByType;
  sort?: string;
  handleSortClick: (sortRule?: T) => void;
} {
  type SortType = T | undefined;
  function isSortByType(s?: string): s is SortType {
    if (!isSortBy) return true;
    return isSortBy(s) || s === undefined;
  }
  function isOrderByType(s?: string): s is OrderByType {
    return s === "asc" || s === "desc";
  }
  const { sort: sortParam = defaultValue } = useSearchParams("sort");
  const updateSearchParams = useUpdateSearchParams<"sort" | "page">();
  let sortBy: SortType;
  let orderBy: OrderByType = "asc";
  if (sortParam) {
    const sortEntry = sortParam.split(",")[0];
    const indexOfPoint = sortEntry.indexOf(".");
    if (indexOfPoint < 0) {
      if (isSortByType(sortEntry)) {
        sortBy = sortEntry;
      }
    } else {
      const sBy = sortEntry.substring(0, indexOfPoint);
      if (isSortByType(sBy)) {
        sortBy = sBy;
        const oBy = sortEntry.substring(indexOfPoint + 1);
        if (isOrderByType(oBy)) {
          orderBy = oBy;
        }
      }
    }
  }
  const sort = sortBy ? `${sortBy}.${orderBy}` : undefined;

  const handleSortClick = (sortRule?: SortType) => {
    if (sortBy === sortRule) {
      if (orderBy === "desc") {
        updateSearchParams((params) => omit({ ...params, sort: `${sortRule}.asc` }, ["page"]), true);
      } else {
        updateSearchParams((params) => omit({ ...params, sort: `${sortRule}.desc` }, ["page"]), true);
      }
    } else {
      updateSearchParams((params) => omit({ ...params, sort: `${sortRule}.desc` }, ["page"]), true);
    }
  };

  return { sortBy, orderBy, sort, handleSortClick };
}
--------------------------------------------------------------------------
// @/utils/object.ts
export function omit<T extends Record<any, any>, U extends keyof T>(obj: T, keys: U[]): Omit<T, U> {
  const newObj = { ...obj };
  keys.forEach((key) => {
    delete newObj[key];
  });
  return newObj;
}
export function omitNil<T extends Record<any, any>>(obj: T): { [K in keyof T]: null extends T[K] ? T[K] | undefined : T[K] } {
  return Object.fromEntries(Object.entries(obj).filter(([, value]) => value != null)) as any;
}

4. 使用方式

// 引入组件
import SortButton from "@/components/SortButton";
// 使用
<SortButton field="transactions" />

5. 效果展示

在这里插入图片描述


总结

下一篇讲【全局常用组件Echarts封装】。关注本栏目,将实时更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/770405.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024最新PyCharm安装教程(附激活码)

今天讲解的是PyCharm安装教程 一、软件简介 PyCharm是一款Python IDE&#xff0c;其带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具&#xff0c;比如&#xff0c; 调试、语法高亮、Project管理、代码跳转、智能提示、自动完成、单元测试、版本控制等等。此外…

鸿翼ECM统一AI检索应用全景,为企业打造全方位搜索服务

随着企业的发展和信息化进程的加快&#xff0c;海量数据已沉淀至企业各类系统&#xff0c;系统间信息孤立、信息利用率低、数据价值无法发挥成为制约企业发展的重要因素。如何对散落在企业各系统中的数据、内容进行统一管理和高效利用&#xff0c;实现高效精准的信息检索&#…

手把手教你一步一步通过AI助手生成利润表分析报告

AI助手之利润表分析报告-操作篇 以下为文字整理部分&#xff1a; 如果要手工制作一份这样的利润分析报告大概要多久时间&#xff1f;从准备数据做成表格&#xff0c;到完成报告&#xff0c;至少需要1天的时间吧&#xff0c;特别是敲文字报告的时候&#xff0c;生怕把数字搞错要…

springboot水环境检测系统的设计与实现-计算机毕业设计041446

摘 要 在我国,水源的污染是不可忽视的问题。对于水质监测进行数据的采集工作,目前主要通过人工实现。因此,部分地区的采集工作,实施起来难度很大,比如恶劣环境和偏僻山区等地。所以,目前对于水质监测的研究,主导方向是建立更加高效完善,智能化的水质监测系统。近几年,无线传感器…

工业 web4.0UI 风格品质卓越

工业 web4.0UI 风格品质卓越

STM32 Cannot access memory

问题描述 最近自己做了一块STM32F103ZET6的板子&#xff0c;在焊接完成后可以在下载器界面看到idcode&#xff0c;但烧录时报错 Cannot access memory 。 解决办法 测量STM32各个供电项&#xff0c;发现时33脚处VDDA电压只有1.8V&#xff0c;是因为R3电阻过大&#xff0c;…

成都欣丰洪泰文化传媒有限公司开网店可靠吗?

在数字化浪潮席卷全球的今天&#xff0c;电商行业无疑是这场浪潮中的佼佼者。而在这波汹涌的电商大潮中&#xff0c;成都欣丰洪泰文化传媒有限公司凭借其专业的电商服务能力和前瞻的市场洞察力&#xff0c;成为了业界的一匹黑马。今天&#xff0c;就让我们一起揭开这家专注于电…

CANoe的capl调用Qt制作的dll

闲谈 因为Qt封装了很多个人感觉很好用的库&#xff0c;所以一直想通过CAPL去调用Qt实现一些功能&#xff0c;但是一直没机会&#xff08;网络上也没看到这方面的教程&#xff09;&#xff0c;这次自己用了两天&#xff0c;踩了很多坑&#xff0c;终于是做成了一个初步的调用方…

Chirp信号生成(FPGA、基于cordic IP核)

一、Chirp生成模块介绍 采用Verilog 生成Chirp&#xff0c;实现输入使能电平&#xff0c;模块输出Chirp信号&#xff0c;Chirp信号频率范围&#xff0c;时间宽度&#xff0c;连续Chirp信号数量可配置。 二、模块例化方法示例 parameter FL d20_000 ; parameter FH…

linux centos tomcat 不安全的HTTP请求方法

1、页面查看 2、在linux主机可使用此命令查看 curl -v -X OPTIONS http://实际地址 3、进入tomcat conf目录vim web.xml&#xff0c;增加以下内容 <!-- close insecure http methods --> <security-constraint><web-resource-collection><web-resource…

springboot项目接入prometheus并展示到grafana

背景 随着业务发展&#xff0c;服务的健康指标变得越来越重要&#xff0c;监控服务的健康指标成为很多公司必要需求。 Prometheus 介绍 Prometheus是一个开源的监控和警报工具集&#xff0c;最初由SoundCloud构建。它自2012年以来已成为许多公司和组织监控其IT基础设施的首选解…

深入了解 Redis 五种类型命令与如何在 Java 中操作 Redis

文章目录 Redis 五种类型命令在 Java 中操作 Redis使用 Spring Data Redis 简化操作 总结 &#x1f389;欢迎来到SpringBoot框架学习专栏~ ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博客&#x1f388;该系列文章专栏&#xff1a;SpringBoot…

【嵌入式CLion】进阶调试——WSL下的Linux体验

说明&#xff1a; 1&#xff0c;这里所指的嵌入式其实是指嵌入式微控制器MCU&#xff0c;即单片机 2&#xff0c;万事开头难&#xff0c;本文目前提供了WSL工具链的搭建&#xff0c;后面会持续更新 一、启用RTOS集成 在搭建WSL工具链之前&#xff0c;先讲一下集成的RTOS功能&a…

汇凯金业:数字货币对经济的影响有哪些

随着信息技术的飞速发展&#xff0c;数字货币作为一种新兴的货币形态&#xff0c;正逐步走进人们的视野&#xff0c;并对传统经济体系产生着深远影响。它不仅革新了交易方式&#xff0c;更在重塑金融格局、赋能经济发展等方面展现出巨大潜力。 一、交易效率的“加速器” 数字…

亚马逊跟卖选品erp采集,跟卖卖家的选品利器,提升选品效率!

今天给亚马逊跟卖卖家&#xff0c;分享我现在在用的两种选品方式&#xff0c;做个铺货或者是跟卖都可以&#xff0c;是不是很多卖家选品现在都是亚马逊前端页面或是新品榜单选择产品跟卖&#xff0c;这样找品这就相当于大海捞针&#xff0c;而且新品榜单的产品你能看到那其他卖…

RS232、RS485与RS422初步学习

目录 电平 传输方式 共模和差模干扰 ps&#xff1a;双绞线 485总线结构 ps&#xff1a;终端电阻 RS232接口&#xff08;DB9&#xff09; 优缺点 RS232优缺点 RS485较RS232的优点 为什么RS232还在使用&#xff1f; 电平 RS232、RS485与RS422的电平 区间逻辑备注RS232…

【技巧】ArcgisPro 字段计算器内置函数方法的调用

在arcgisPro中&#xff0c;内置了常用的几种函数方法&#xff0c;如顺序编号&#xff0c;重分类等&#xff1b;调用方法如下&#xff1a;

python-计算矩阵边缘元素之和(赛氪OJ)

[题目描述] 输入一个整数矩阵&#xff0c;计算位于矩阵边缘的元素之和。 所谓矩阵边缘的元素&#xff0c;就是第一行和最后一行的元素以及第一列和最后一列的元素。输入&#xff1a; 输入共 m 1 行。 第一行包含两个整数 m, n (1 < m,n < 100) &#xff0c;分别为矩阵的…

如何学习大型语言模型中的量化

前言 图片来自作者&#xff1a;Flow 展示了量化的必要性。&#xff08;笑脸和生气脸图片来自Yan Krukau) 在我解释上面的图表之前&#xff0c;让我先介绍一下您将在本文中学习的重点内容。 首先&#xff0c;您将了解量化是什么以及为什么量化。接下来&#xff0c;您将深入了解…

万亿赛道上的新星:向量数据库如何成为AI大模型的超级引擎

万亿赛道上的新星:向量数据库如何成为AI大模型的超级引擎? 在这个人工智能飞速发展的时代,向量数据库正以其独特的能力,成为AI大模型技术底座的超级引擎。从开源社区的繁荣到云计算的广泛应用,向量数据库的每一步发展都显得尤为重要。 一、初创引领,巨头跟进:向量数…