React 封装组件的一些心得(一)

2023-11-09

起因

最近公司业务不是那么多,就抽空写了下组件库的东西,然后看了很多组件库的源码,由于我这里封装的主要是 taro 移动端的组件,所以主要是参考了 antd-mobile , react-vant, tard 等组件库。

然后根据他们的源码,整合出一套自己比较喜欢的组件封装写法,分享给大家。

文末附上组件快捷代码片段

例:封装一个计数器组件

样式

react 中没有像 vue 那样的 scope ,首先一个组件需要防止类名和其他的重复。

定义一个类名前缀 这里可以统一命名一个自己喜欢的开头,我这里就叫 com

const classPrefix = `com-count`;

html 中可以这样使用,withNativeProps 的详细描述放后面了(可先忽略)

  return withNativeProps(
    ret,
    <div className={classPrefix}>
      <h4 className={`${classPrefix}-title`}>{title}</h4>
      <div className={`${classPrefix}-count`}>{c}</div>
      <button onClick={() => {setC(c => ++c)}}>+1</button>
    </div>
  )

index.less 文件中

// @import '@/style/index.less'; // 这里可以引入全局的一些样式
@class-prefix: ~'com-count';
.@{class-prefix} {
  width: 100px;
  background-color: #f2f2f2;
  &-title {
    font-weight: bold;
  }
  &-count {
    color: skyblue;
  }
}

props

生成组件的 props 类型,NativeProps 类型的详细描述放后面了(可先忽略)

export type CountProps = { 
  count: number
  title?: string
} & NativeProps

定义组件的默认值

const defaultProps = {
  title: '计数器',
}
type RequireType = keyof typeof defaultProps

props 的使用,useMergeProps 就是用来合并 props 默认值的,详细描述放后面了

const Count = (comProps: CountProps) => {
  const props = useMergeProps<CountProps, RequireType>(comProps, defaultProps)
  
  const { title, ...ret } = props
  
  return <div>{title}<div/>
}

完整案例使用

  • demo
import { useState } from "react";
import Count from ".."
export default () => {
  const [count, setCount] = useState(0);
  return (
    <Count count={count} className='count' style={{background: '#f2f2f2'}} />
  )
}
  • 组件
import React, { useState, useEffect } from 'react';
import './index.less';
import { NativeProps, withNativeProps } from '@/utils/native-props';
import useMergeProps from '@/hooks/use-merge-props';

const classPrefix = `com-count`;

// 组件 props
export type CountProps = { 
  count: number
  title?: string
} & NativeProps

// props 默认值
const defaultProps = {
  title: '计数器',
  count: 0,
}
type RequireType = keyof typeof defaultProps

const Count = (comProps: CountProps) => {
  // 合并 props
  const props = useMergeProps<CountProps, RequireType>(comProps, defaultProps)
  const { count, title, ...ret } = props

  const [c, setC] = useState(count);

  useEffect(() => {
    setC(count)
  }, [count])
  
  // withNativeProps 可以用来合并传入的 classname 和 styles 等
  return withNativeProps(
    ret,
    <div className={classPrefix}>
      <h4 className={`${classPrefix}-title`}>{title}</h4>
      <div className={`${classPrefix}-count`}>{c}</div>
      <button onClick={() => {setC(c => ++c)}}>+1</button>
    </div>
  )
}

export default Count

utils 和 hooks 等的引入方式

NativeProps 和 withNativeProps

该方法的从 antd 组件库的源码中借鉴过来使用的。

import React from 'react';
import type { CSSProperties, ReactElement } from 'react';
import classNames from 'classnames';

// style 和 className 的类型,根据需要可以加其他东西,如 onClick 等
export type NativeProps<S extends string = never> = {
  className?: string;
  style?: CSSProperties & Partial<Record<S, string>>;
}

// 可以用来合并传入的 classname 和 styles 等
export function withNativeProps<P extends NativeProps>(props: P, element: ReactElement) {
  const p = {
    ...element.props,
  };
  if (props.className) {
    p.className = classNames(element.props.className, props.className);
  }
  if (props.style) {
    p.style = {
      ...p.style,
      ...props.style,
    };
  }
  return React.cloneElement(element, p);
}
  • index.less
// @import '../style/index.less';
@class-prefix: ~'com-count';
.@{class-prefix} {
  width: 100px;
  &-title {
    font-weight: bold;
  }
  &-count {
    color: skyblue;
  }
}

useMergeProps

该钩子是从 arco-design 借鉴过来改进的。

import { useMemo } from 'react';
import omit from '@/utils/omit';

export type MergePropsOptions = {
  _ignorePropsFromGlobal?: boolean;
};

/** 将某些属性变为必选 */
type RequireKey<T, K extends keyof T> = Omit<T, K> & { [P in K]-?: T[P] }

export default function useMergeProps<PropsType, K extends keyof PropsType>(
  componentProps: PropsType & MergePropsOptions,
  defaultProps: Partial<PropsType>,
  globalComponentConfig: Partial<PropsType> = {}
): RequireKey<PropsType, K> {
  const { _ignorePropsFromGlobal } = componentProps;
  const _defaultProps = useMemo(() => {
    return { ...defaultProps, ...(_ignorePropsFromGlobal ? {} : globalComponentConfig) };
  }, [defaultProps, globalComponentConfig, _ignorePropsFromGlobal]);

  const props = useMemo(() => {
    const mProps = omit(componentProps, ['_ignorePropsFromGlobal']) as PropsType;

    for (const propName in _defaultProps) {
      if (mProps[propName] === undefined) {
        mProps[propName] = _defaultProps[propName]!;
      }
    }

    return mProps;
  }, [componentProps, _defaultProps]);

  return props as RequireKey<PropsType, K>;
}

omit

/** 删除一个对象中的key */
export default function omit<T extends object, K extends keyof T>(
  obj: T,
  keys: Array<K | string> // string 为了某些没有声明的属性被omit
): Omit<T, K> {
  const clone = {
    ...obj,
  };
  keys.forEach((key) => {
    if ((key as K) in clone) {
      delete clone[key as K];
    }
  });
  return clone;
}

配置用户代码片段

com快捷.gif com-快捷2.gif

配置位置

image.png

这里输入名称 typescriptreact 创建就可以了。

image.png

往里面加入以下 json 数据

"tsxcomreact": {
  "prefix": "tsxcomreact",
  "body": [
    "import React, { useState, useEffect } from 'react';",
    "import './index.less';",
    "import { NativeProps, withNativeProps } from '@/utils/native-props````
    "import useMergeProps from '@/hooks/use-merge-props';",
    "",
    "const classPrefix = `com${2}-${1}`;",
    "",
    "export type ${1}Props = { ",
    "",
    "} & NativeProps",
    "",
    "const defaultProps = {",
    "  ",
    "}",
    "type RequireType = keyof typeof defaultProps",
    "",
    "const ${1} = (comProps: ${1}Props) => {",
    "  const props = useMergeProps<${1}Props, RequireType>(comProps, defaultProps)",
    "  const { ...ret } = props",
    "  ",
    "  return withNativeProps(",
    "    ret,",
    "    <div className={classPrefix}>",
    "      ",
    "    </div>",
    "  )",
    "}",
    "",
    "export default ${1}"
  ],
  "description": "Log output to console"
},
"cdivclass": {
  "scope": "typescriptreact",
  "prefix": "cdc",
  "body": [
    "<div className={`\\${classPrefix}-${0}`}></div>"
  ],
  "description": "Log output to console"
},

结语

如果有大佬看到这里,希望能给点意见和改进方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React 封装组件的一些心得(一) 的相关文章

随机推荐

  • UE4_C++访问蓝图里的变量

    有没有碰到这样的问题 之前的同事用 连连看 实现了项目的逻辑 后续你要维护推展项目的开发 但头疼的是 你是个coder not 连连看 玩家 这时候how to do it UE4 C 访问蓝图里的变量 c 获得BP的方式 在访问蓝图变量之
  • GTest基础学习-04-第3个单元测试-测试夹具test fixture

    这篇来学习一下Gtest中更高级一些的特性test fixture 测试夹具的基本上使用 什么的场景需要使用到测试夹具呢 测试夹具是哪个宏 这篇来学习这个主题 1 什么叫test fixture 什么是测试夹具 这个概念在任何xUnit系列
  • 工厂车间设备智能化管理系统软件

    工厂车间设备智能化管理系统软件 在面对疫情这样的严峻挑战下 制造业企业和工厂开始走向精细化和智能化的管理模式 如今有不少的工厂采用智能化的管理模式 统筹人事 客户 采购 订单 车间 物料 仓储 工艺等板块 而这样的一款智能工厂管理系统 应该
  • C++:这门语言优势在哪?命名空间以及缺省参数?

    文章目录 C 的优势 解决命名空间的问题 缺省参数 C 的优势 C 和C语言比起来有许多优势 这里我们先举一个例子 后续进行补充 解决命名空间的问题 首先看这样的代码 include
  • docker ubuntu 使用apt安装vim--报错Unable to locate package vim

    docker ubuntu 安装vim 报错Unable to locate package vim 前言 想修改从vulhub拉取运行的docker容器里的配置文件 使用vim时报错bash vim command not found 发
  • nest:[TypeOrmModule] Unable to connect to the database. Retrying

    有可能是刚开机的时候 mysql服务还没有开启导致的
  • 百度 AI Studio——《高层API助你快速上手深度学习》课程学习1

    百度 AI Studio 高层API助你快速上手深度学习 课程学习1 该系列文章系个人读书笔记及总结性内容 任何组织和个人不得转载进行商业活动 相关链接 飞桨 飞桨开源框架 PaddlePaddle 是一个易用 高效 灵活 可扩展的深度学习
  • c++学生信息管理系统(window控制台实现鼠标点击操作)

    翻起大一时写过的作业代码 一个学生信息管理系统 当时不会使用QT 不会MFC等库 只会c 但是又想做一个有界面的 能够实现鼠标操作的程序 于是绞尽脑汁查资料 自己造轮子 最终写出来了下面的这个现在连我自己也看不懂的代码 代码虽然有些长 单文
  • 图书管理系统服务器,图书管理系统的服务器

    图书管理系统的服务器 内容精选 换一换 eBackup备份管理系统支持对VMware环境下虚拟机的保护 您需要在系统中增加VMware受保护环境 从而对受保护环境中的虚拟机进行备份和恢复 增加VMware受保护环境时 如果虚拟机名称中包含
  • 回文序列

    题目描述 如果一个数字序列逆置之后跟原序列是一样的就称这样的数字序列为回文序列 例如 1 2 1 15 78 78 15 112 是回文序列 1 2 2 15 78 87 51 112 2 11 不是回文序列 现在给出一个数字序列 允许使用
  • Unity3D的断点调试功能

    Unity3D的断点调试功能 2013 03 14 16 27 51 分类 Unity教程 标签 unity monodevelop 断点调试 debug 举报 字号 订阅 断点调试功能可谓是程序员必备的功能了 Unity3D支持编写js和
  • C#获取字符串中括号中内容的正则表达式

    正则表达式为 lt w 2 如 string str1 发送奥点奥点 66 dasaa213434esdf Regex rex new Regex lt w 2 Match m rex Match str1 Console WriteLin
  • 【自然语言处理】ChatGPT 相关核心算法

    ChatGPT 相关核心算法 ChatGPT 的卓越表现得益于其背后多项核心算法的支持和配合 本文将分别介绍作为其实现基础的 Transformer 模型 激发出其所蕴含知识的 Prompt Instruction Tuning 算法 其涌
  • 使用jq让页面滚动到顶部

    以下是完整代码
  • 求任何时间下不同纬度太阳高度角的计算公式

    太阳高度角简称太阳高度 其实是角度 对于地球上的某个地点 太阳高度是指太阳光的入射方向和地平面之间的夹角 太阳 高度是决定地球表面获得太阳热能数量的最重要的因素 我们用h来表示这个角度 它在数值上等于太阳在天球地平坐标系中的地平高度 太阳高
  • 【111】支持向量机原理及python实现

    内容目录 一 基本概念二 鸢尾花实验三 乳腺癌检测实验 一 基本概念 024 SVM有监督学习LinearSVC LinearSVR SVC SVR参数解释 线性支持向量机 在超平面确定的情况下 可以相对地表示点距离超平面的远近 对于两类分
  • sublime Text3 自定义配色方案

    自定义配色方案地址 使用引导 首先 点击General gt background gt 修改一般 txt文件的默认背景 护眼的RGB值 豆沙绿的rgb颜色 199 237 204 色调 85 饱和度 1 2 3 亮度 2 0 5 对应成1
  • 504. Base 7

    Given an integer return its base 7 string representation Example 1 Input 100 Output 202 Example 2 Input 7 Output 10 Note
  • Mysql实现行列转换

    mysql数据库如何实现行列转换 1 行转列 方案一 select name sum case when course java then grade end as java sum case when course C then grad
  • React 封装组件的一些心得(一)

    起因 最近公司业务不是那么多 就抽空写了下组件库的东西 然后看了很多组件库的源码 由于我这里封装的主要是 taro 移动端的组件 所以主要是参考了 antd mobile react vant tard 等组件库 然后根据他们的源码 整合出