React组件封装

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

react组件封装


一、基础组件封装

1.1.Input

import { Input, message } from "antd";

const DeliveryInput = ({
  value,
  onChange,
  maxNum,
  item,
  handleOrderList,
}: any) => {
  const [messageApi, contextHolder] = message.useMessage();
  const handleChange = (e: any) => {
    const inputValue = e.target.value;
    if (/^[1-9][0-9]*$/.test(inputValue) && inputValue.length <= 4) {
      if (maxNum) {
        if (Number(inputValue) > maxNum) {
          messageApi.warning("已经是最大值了!");
        } else {
          onChange(Number(inputValue));
          handleOrderList &&
            handleOrderList(
              item.dataIndex,
              item.entity?.itemId,
              inputValue,
              item.entity,
            );
        }
      } else {
        onChange(Number(inputValue));
        handleOrderList &&
          handleOrderList(
            item.dataIndex,
            item.entity?.itemId,
            inputValue,
            item.entity,
          );
      }
    } else if (inputValue === "") {
      onChange("");
      handleOrderList &&
        handleOrderList(
          item.dataIndex,
          item.entity?.itemId,
          inputValue,
          item.entity,
        );
    }
  };
  return (
    <>
      {contextHolder}
      <Input
        style={{
          width: "100%",
          height: "100%",
          color: "#F29D39",
        }}
        type="number"
        value={value}
        onChange={handleChange}
        placeholder={"请输入货期"}
      />
    </>
  );
};

export default DeliveryInput;

1.2.Loading

import { styled } from "@umijs/max";
import { Spin } from "antd";
import React from "react";

const LoadingBox = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  zindex: 999;
  .content {
    padding: 50px;
    background: rgba(0, 0, 0, 0);
    border-radius: 4px;
  }
`;

interface ILoading {
  size?: "small" | "default" | "large";
  spinning?: boolean;
  boxStyle?: React.CSSProperties | undefined;
  loadingText?: string;
}
/**
 *
 * @param size loading大小, small default large
 * @param spinning 是否为加载中状态
 * @param boxStyle 容器自定义样式
 * @param loadingText 加载提示文本
 */

const Loading: React.FC<ILoading> = ({
  size,
  spinning,
  boxStyle,
  loadingText,
}): React.ReactElement => {
  return (
    <LoadingBox style={boxStyle}>
      <Spin spinning={spinning} size={size} tip={loadingText}>
        {spinning ? <div className="content" /> : <></>}
      </Spin>
    </LoadingBox>
  );
};
export default Loading;

1.3.Upload(图片限制,可选裁切)

import { PlusOutlined } from "@ant-design/icons";
import type { UploadFile, UploadProps } from "antd";
import { Image as ImageAntd, Upload, message } from "antd";
import ImgCrop from "antd-img-crop";
import { RcFile } from "antd/es/upload";
import { useState } from "react";
const getBase64 = (img: RcFile, callback: (url: string) => void) => {
  const reader = new FileReader();
  reader.addEventListener("load", () => callback(reader.result as string));
  reader.readAsDataURL(img);
};

const base64ToBlob = (url: any, imageType: string | undefined) => {
  const arr = url.split(",");
  const bstr = atob(arr[1]);
  let n = bstr.length;
  const u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: imageType });
};

/**
 *
 * @param setFileFormData 设置接口FormData数据
 * @param setBase64ToBlobObj 设置接口FormData的图片参数setBase64ToBlob及fileName
 * @param fileList 接口返回的图片列表
 * @param uploadButtonText 上传按钮文字
 * @param restrictWidth 宽度限制
 * @param restricHeight 高度限制
 * @param type formData自命名图片名字前缀
 * @param size 图片大小限制
 * @param aspectWidth 图片裁剪比例宽度设置
 * @param sizeHeight 图片裁剪比例高度设置
 */

const UploadImage = ({
  setFileFormData,
  setBase64ToBlobObj,
  id,
  fileList: fileListArr,
  uploadButtonText,
  restrictWidth,
  restricHeight,
  type,
  size,
  aspectWidth,
  sizeHeight,
}: any) => {
  const [messageApi, contextHolder] = message.useMessage();
  const [previewOpen, setPreviewOpen] = useState(false);
  const [previewImage, setPreviewImage] = useState("");
  const [fileList, setFileList] = useState<UploadFile[]>(fileListArr || []);

  const validateImageSize = (file: File) => {
    return new Promise((resolve, reject) => {
      getBase64(file as RcFile, async (url) => {
        const image = new Image();
        image.onload = () => {
          const { width, height } = image;
          if (width === restrictWidth && height === restricHeight) {
            resolve(true);
          } else {
            messageApi.error("图片尺寸不符合要求,请重新上传");
            resolve(false);
          }
        };
        image.onerror = reject;
        image.src = url;
      });
    });
  };

  // 限制图片
  const beforeUpload: any = async (file: RcFile) => {
    const isJpgOrPng = file.type === "image/jpeg" || file.type === "image/png";
    if (!isJpgOrPng) {
      messageApi.error("请上传JPG/PNG图片!");
    }
    const isLtSizeMB = file.size / 1024 / 1024 < (size || 6);
    if (!isLtSizeMB) {
      messageApi.error(`请上传大小为${size || 6}MB以内的图片!`);
    }
    if (restrictWidth || restricHeight) {
      return isJpgOrPng && isLtSizeMB;
    } else {
      const limitSize = await validateImageSize(file);
      return isJpgOrPng && isLtSizeMB && limitSize;
    }
  };

  const handleChange: UploadProps["onChange"] = (info) => {
    if (info.file?.originFileObj) {
      setFileList(info.fileList);
      getBase64(info.file.originFileObj as RcFile, async (url) => {
        setPreviewImage(url);
        const formData = new FormData();
        if (id) formData.append("id", id);
        formData.append(
          type,
          base64ToBlob(url, info.file.type),
          `${type}` + Date.now() + "." + info.file.type?.split("/")[1],
        );
        setFileFormData?.(formData);
        setBase64ToBlobObj?.({
          base64ToBlob: base64ToBlob(url, info.file.type),
          fileName:
            `${type}` + Date.now() + "." + info.file.type?.split("/")[1],
        });
      });
    }
  };

  const handlePreview = async () => {
    if (fileList?.[0]?.url) {
      setPreviewImage(fileList?.[0]?.url);
    }
    setPreviewOpen(true);
  };

  const uploadButton = (
    <button style={{ border: 0, background: "none" }} type="button">
      <PlusOutlined />
      <div style={{ marginTop: 8, fontWeight: 400 }}>
        {uploadButtonText || "上传LOGO"}
      </div>
    </button>
  );

  return (
    <>
      {contextHolder}
      <ImgCrop
        rotationSlider
        // aspectSlider  // 图片旋转
        aspect={aspectWidth || 1 / sizeHeight || 1}
      >
        <Upload
          name="avatar"
          listType="picture-card"
          className="avatar-uploader"
          fileList={fileList}
          action="/upload.do"
          beforeUpload={beforeUpload}
          onChange={handleChange}
          onPreview={handlePreview}
          onRemove={() => {
            setBase64ToBlobObj?.(null);
            setFileFormData?.(null);
            setFileList([]);
            setPreviewImage("");
          }}
        >
          {fileList.length >= 1 ? null : uploadButton}
        </Upload>
      </ImgCrop>
      {previewImage && (
        <ImageAntd
          wrapperStyle={{ display: "none" }}
          preview={{
            visible: previewOpen,
            onVisibleChange: (visible) => setPreviewOpen(visible),
            // afterOpenChange: (visible) => !visible && setPreviewImage(""),
          }}
          src={previewImage}
        />
      )}
    </>
  );
};

export default UploadImage;

总结

以上就是今天要讲的内容,本文主要记录开发常用的React封装的组件。
愿:平安顺遂!

React 组件封装成 Hook 可以让我们更方便地在函数组件中复用组件逻辑。下面是一些封装组件为 Hook 的示例代码: 1. 使用 useState 封装计数器组件: ```javascript import { useState } from 'react'; const useCounter = (initialCount = 0, step = 1) => { const [count, setCount] = useState(initialCount); const increment = () => setCount(count + step); const decrement = () => setCount(count - step); return { count, increment, decrement }; }; export default useCounter; ``` 使用示例: ```javascript import useCounter from './useCounter'; function Counter() { const { count, increment, decrement } = useCounter(); return ( <> <p>Count: {count}</p> <button onClick={increment}>+</button> <button onClick={decrement}>-</button> </> ); } ``` 2. 使用 useEffect 封装鼠标位置跟踪组件: ```javascript import { useState, useEffect } from 'react'; const useMousePosition = () => { const [position, setPosition] = useState({ x: 0, y: 0 }); useEffect(() => { const updateMousePosition = (event) => { setPosition({ x: event.clientX, y: event.clientY }); }; document.addEventListener('mousemove', updateMousePosition); return () => { document.removeEventListener('mousemove', updateMousePosition); }; }, []); return position; }; export default useMousePosition; ``` 使用示例: ```javascript import useMousePosition from './useMousePosition'; function MouseTracker() { const { x, y } = useMousePosition(); return ( <p> Mouse position: {x}, {y} </p> ); } ``` 以上是两个简单的示例,通过封装 React 组件为 Hook,可以让我们更好地复用组件逻辑并在函数组件中使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值