React UI组件封装实战——以经典项目「个人博客」与「仿手机QQ」为例

UI组件封装是前端开发常用的代码复用手段,而「个人博客」与「仿手机QQ」是前端初学者常见的两个经典项目。本文内容来源于笔者对这两个经典项目在React框架下的个人实现,旨在展示笔者对React UI组件封装的理解,供读者参考。项目使用的基础UI组件库为Ant Desgin,封装的组件均为跨页面使用的组件。

一、「个人博客」项目

1.1 健壮图像 RobustImg

网页中引用的图像有时并不存在,此时<img>元素将显示为一个×。如果能用一张带有错误提示信息的图像替换它,就不会破坏页面布局,对用户也更为友好。

js 复制代码
import { useState, useEffect } from 'react';
import DefaultImg from "../assets/error.png";

export default function RobustImg({src, style = {}}) {
    const [imgSrc, setImgSrc] = useState(DefaultImg);
    //尝试加载,成功则替换默认图片
    useEffect(() => {
        const image = new Image();
        image.src = src;
        image.onload = () => setImgSrc(src);
        image.onerror = () => console.log(new Error('Could not load image at ' + src));
    }, [src]);
    return <img src={imgSrc} style={style} alt="" />
}

1.2 删除按钮 DelBtn

删除属于危险操作,与其他按钮相比,通常需要增加一步确认。为了减少重复代码,可将删除按钮单独封装,传入处理函数{onConfirm}作为props。

js 复制代码
import { Button, Popconfirm } from "antd";
import { DeleteOutlined } from "@ant-design/icons";

const DelBtn = ({onConfirm}) => {
    return (
        <Popconfirm okText="确认" cancelText="取消"
            title="确认删除?" onConfirm={onConfirm}
        >
            <Button type="primary" danger shape="circle" icon={<DeleteOutlined />} />
        </Popconfirm>
    )
}

export default DelBtn;

1.3 专栏下拉选择器 ColumnSelect

Select(下拉)选择器常用于可选项较多的场景,比如本项目中博客文章的专栏选择。如果某类数据的选择器在不同页面中多次出现,且选项动态生成,此时应将其封装为独立组件,并根据使用需求传入定制化参数作为props。

js 复制代码
import { useState, useEffect } from "react";
import { Form, Select } from "antd";
import { http } from '../utils'; //http是axios.create返回的对象

const ColumnSelect = ({multiple = false, defaultValue = ''}) => {
    const [columns, setColumns] = useState([]);
    const [value, setValue] = useState(defaultValue);
    useEffect(() => {
        async function fetch() {
            const res = await http.get('/columns');
            setColumns(res.data);
        }
        fetch();
    }, []);

    return (
        <Form.Item label="专栏" name="columns">
            <Select
                mode = { multiple ? 'multiple' : '' }
                allowClear = { multiple }
                value = {value} onChange = {v => setValue(v)}
                defaultValue = {defaultValue} disabled = {defaultValue}
                placeholder = "选择专栏"
                options = {[
                    { value: '', label: '不限' },
                    ...columns.map(column => ({ value: column.name, label: column.name }))
                ]}
            />
        </Form.Item>
    );
}

export default ColumnSelect;

如果网站仅有一级导航,可以统一封装Breadcrumb面包屑组件,以{subtitle}作为外层组件的props。

js 复制代码
import { Breadcrumb } from 'antd';
import { Link } from 'react-router-dom';

const BreadcrumbTitle = ({subtitle}) => {
    return <Breadcrumb separator=">" items={[{ title: <Link to="/">首页</Link> }, { title: subtitle }]}/>
}

export default BreadcrumbTitle;

二、「仿手机QQ」项目

2.1 「更多」气泡弹出框 MorePopover

在移动APP右上角的区域,经常可以看见一个⊕按钮,点击它会弹出气泡式的菜单Popover.Menu。各个Popover入口的样式可以统一(比如使用MoreOutline),核心区别之处在于菜单的内容(actions)与点击处理函数(onAction)。actions与onAction紧密相关,可以定义在一个对象中,作为props传入自定义组件。

js 复制代码
import { Popover } from 'antd-mobile';
import { MoreOutline } from 'antd-mobile-icons';

const MorePopover = ({moreActions}) => {
    return (
        <Popover.Menu
            actions={moreActions.actions}
            placement='bottom-start'
            onAction={moreActions.onAction}
            trigger='click'
        >
            <MoreOutline />
        </Popover.Menu>
    );
}

export default MorePopover;

2.2 消息时间 MsgTime

在手机QQ中,消息因发送时间不同显示为多种格式。简单起见,接下来只考虑两种情况:距当前时间不超过24小时的,时间格式为"HH:mm";否则时间格式为"MM/dd"。由于JS原生不支持日期格式字符串,时间数据的转换略显繁琐,因此应该自定义一个独立组件。

js 复制代码
const MsgTime = ({timestamp, brief = false}) => {
    if(!brief) return <span>{new Date(timestamp).toLocaleString()}</span>;
    const msgTime = new Date(timestamp);
    const timeDiff = new Date() - msgTime;
    const hoursDiff = timeDiff / (1000 * 60 * 60); 
    return <div> { hoursDiff <= 24 
        ? msgTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false })
        : msgTime.toLocaleDateString([], { month: '2-digit', day: '2-digit' })
    } </div>;
}

export default MsgTime;
相关推荐
chilavert3182 小时前
技术演进中的开发沉思-277 AJax :Calendar
前端·javascript·microsoft·ajax
chilavert3182 小时前
技术演进中的开发沉思-276 AJax : Menu
javascript·ajax·交互
debug 小菜鸟2 小时前
搭建web 环境的那些事
前端
鹏多多2 小时前
Flutter下拉刷新上拉加载侧拉刷新插件:easy_refresh全面使用指南
android·前端·ios
Mike_jia2 小时前
OpenDeRisk:AI 原生风险智能系统 ——7*24H 应用系统AI数字运维助手(AI-SRE)
前端
许泽宇的技术分享2 小时前
当AI遇见UI:A2UI协议在.NET Blazor中的完整实现与深度剖析
人工智能·ui·.net·a2ui
朱穆朗2 小时前
electron升级到33.0.x版本后,devtools字体的修改方法
前端·javascript·electron
2501_944452233 小时前
智能洞察 Cordova 与 OpenHarmony 混合开发实战
javascript
IT_陈寒3 小时前
Java 21新特性实战:5个必学的性能优化技巧让你的应用提速40%
前端·人工智能·后端