React+TS前台项目实战(六)-- 全局常用组件Button封装


前言

今天这篇主要讲全局按钮组件封装,可根据UI设计师要求自定义修改。


Button组件

1. 功能分析

(1)可以通过className属性自定义按钮样式,传递样式类名来修改按钮的样式
(2)是否可点击由disabled属性控制,当disabled为true时,按钮被禁用
(3)加载状态由loading属性控制,当loading为true时,按钮显示加载动画
(4)当点击事件回调是Promise函数,执行后续处理;否则直接调用click点击事件

2. 代码+注释说明

// @/components/Button/index.tsx
import { useState } from "react";
import classNames from "classnames";
import styles from "./index.module.scss";

// 组件的属性类型
type Props = {
  // 按钮的文本
  text: string;
  // 自定义的类名
  className?: string;
  // 是否禁用按钮
  disabled?: boolean;
  // 是否显示加载动画
  loading?: boolean;
  // 点击按钮时的回调函数
  click?: () => void;
  beforeChange?: (() => Promise<any>) | undefined;
};

// 按钮组件
export default (props: Props) => {
  // 解构属性
  const { text, className, disabled, loading, beforeChange, click } = props;
  const [load, setLoad] = useState(false);
  /**
   * 点击按钮时的事件处理函数
   * - 如果按钮被禁用,则直接返回
   * - 如果 beforeChange 是一个Promise函数,则调用其后续处理
   * - 否则直接调用 click
   */
  const handleClick = () => {
    if (disabled) return undefined;
    const isFunction = Object.prototype.toString.call(beforeChange) === "[object Function]";
    if (!isFunction) {
      click?.();
      return false;
    }
    // 启用加载动画
    setLoad(true);
    beforeChange?.().finally(() => setLoad(false));
  };
  return (
    // 按钮元素
    <button
      type="button"
      // 设置类名
      className={classNames(
        styles.container,
        // 禁用或加载时增加特定的类名
        (disabled || loading) && styles.isDisabled,
        className
      )}
      // 禁用时禁用快捷键操作
      onKeyDown={handleClick}
      // 禁用时禁用点击事件
      onClick={handleClick}
    >
      {/* 加载动画 */}
      {loading && load && <i className={`${styles.loading} iconfont icon-loading`}></i>}
      {/* 按钮文本 */}
      <span>{text}</span>
    </button>
  );
};
------------------------------------------------------------------------------
// @/components/Button/index.module.scss
.container {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 40px;
  color: #fff;
  background-color: var(--cd-primary-color);
  border-radius: 4px;
  border: none;
  cursor: pointer;
  span {
    font-size: 14px;
    line-height: 14px;
  }
  &:hover {
    background-color: var(--cd-primary-color);
  }
  .isDisabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
  @keyframes rotate {
    0% {
      transform: rotate(0deg);
    }
    50% {
      transform: rotate(180deg);
    }
    100% {
      transform: rotate(360deg);
    }
  }
  .loading {
    font-size: 24px;
    animation: rotate 2s linear infinite;
  }
}

3. 使用方式

// 引入组件
import Button from "@/components/Button";
// 有加载动画使用方式
<Button text="有loading" loading={true} beforeChange={onDoneChange}></Button>
// 点击按钮触发loading
const onDoneChange = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("onDoneChange");
      resolve(true);
    }, 2000);
  });
};

-----------------------------------------------------------------------------------------------------

// 无加载动画使用方式
<Button text="有loading" loading={false} beforeChange={onDoneClick}></Button>
// 点击按钮不触发loading
const onDoneClick = () => {
  console.log("onDoneClick");
};

4. 效果展示

(1)有加载动画,执行promise函数

在这里插入图片描述
在这里插入图片描述

(2)无加载动画,执行click事件

)


总结

下一篇讲【全局模态框Modal组件、公共弹窗Dialog组件封装】。关注本栏目,将实时更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值