React动态添加标签组件

这篇具有很好参考价值的文章主要介绍了React动态添加标签组件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

在前端开发的过程中,一些表单的输入经常需要输入多个内容,如果采用一个输入框+逗号分隔的方式,展示起来不是很清晰,一般需要采用标签的方式

需求

React动态添加标签组件,react.js,前端,javascript,html,ecmascript

React动态添加标签组件,react.js,前端,javascript,html,ecmascript

  1. 可以指定空状态时的标题

  2. 设置标签颜色

  3. 每个标签的最大长度(字符数)

  4. 接口传递的时候的分隔标记(是用逗号,还是其他)

  5. 直接处理表单,不需要二次处理

所以需要传入以下内容给该组件

  • title:标题

  • separator:分隔标记

  • maxLength:最大长度

  • color:颜色

  • form,name:处理的表单和对应的字段

const { title = '新增一个', separator = ',', maxLength = 40, color = 'orange', form, name } = props;

TagInput.propTypes = {
  title: PropTypes.string, // 新增一个tag的标题
  separator: PropTypes.string, // 分隔符
  maxLength: PropTypes.number, // tag最大长度
  color: PropTypes.string, // tag颜色
  form: PropTypes.object, // form
  key: PropTypes.string, // form的key
};

代码编写

是否显示输入框

首先需要有一个虚线框的标签

<Tag style={{ background: '#fff', borderStyle: 'dashed' }}>
    <PlusOutlined /> {title}
</Tag>

点击后出现文本输入框

<Input type="text" size="small" style={{ width: 78 }} />

并且锚定这个输入框(出现输入光标)

所以需要有一个状态记录是否显示输入框

const [inputVisible, setInputVisible] = useState(false); // 是否显示输入框

所以上述代码变为:

const saveInputRef = useRef();

useEffect(() => {
  if (inputVisible) saveInputRef.current.focus();
}, [inputVisible]);

{inputVisible && (
  <Input ref={saveInputRef} type="text" size="small" style={{ width: 78 }} />
)}
{!inputVisible && (
  <Tag onClick={() => setInputVisible(true)} style={{ background: '#fff', borderStyle: 'dashed' }}>
    <PlusOutlined /> {title}
  </Tag>
)}

useEffect监听输入框是否出现,如果出现,则锚定「saveInputRef.current.focus()」

添加一个标签

为了记录输入框的内容定义一个新的变量

const [inputValue, setInputValue] = useState(''); // 输入框的值

<Input ref={saveInputRef} type="text" size="small" style={{ width: 78 }} value={inputValue} onChange={(e) => setInputValue(e.target.value)} />

每次输入内容都会修改inputValue的值

因为有多个标签,先定义一个变量来记录我们已经添加的标签

const [tags, setTags] = useState([]); // 待分隔列表

当鼠标在输入框外部点击或者敲击回车的时候,都需要添加一个标签

所以需要给输入框添加onBlur和onPressEnter方法

<Input
  ref={saveInputRef}
  type="text"
  size="small"
  style={{ width: 78 }}
  value={inputValue}
  onChange={(e) => setInputValue(e.target.value)}
  onBlur={handleInputConfirm}
  onPressEnter={handleInputConfirm}
/>

编写添加标签的方法:handleInputConfirm

  • 拿到之前的标签+本次输入的,一起放到tags变量中

  • 给表单设置一下这个值(用分隔标记拼接起来)

  • 隐藏输入框

  • 清空输入框

/*
 * 新增一个tag
 * */
const handleInputConfirm = () => {
  if (inputValue && tags.indexOf(inputValue) === -1) {
    const newTags = [...tags, inputValue];
    setTags(newTags);
    form.setFieldsValue({ [name]: newTags?.join(separator) });
  } else {
    message.error('请正确输入');
  }
  setInputVisible(false);
  setInputValue('');
};

展示标签

在上述步骤之后,tags中已经添加了我们的标签了,将它展示出来

  • 判断字符串长度,如果大于我们配置的最大长度则裁剪,没有则全部展示

  • 超长的标签增加一个气泡提示,鼠标移动上去后可以看到全部内容

{tags.map((tag) => {
  const isLongTag = tag.length > maxLength;
  const tagElem = (
    <Tag key={tag} color={color}>
      {isLongTag ? `${tag.slice(0, maxLength)}...` : tag}
    </Tag>
  );
  return isLongTag ? (
    <Tooltip title={tag} key={tag}>
      {tagElem}
    </Tooltip>
  ) : (
    tagElem
  );
})}

删除标签

给Tag设置closable和onClose方法

const tagElem = (
  <Tag key={tag} closable onClose={() => handleClose(tag)} color={color}>
    {isLongTag ? `${tag.slice(0, 20)}...` : tag}
  </Tag>
);

handleClose方法:

  • 过滤tags中不需要的tag并更新

  • 重新给表单对应的键值对赋值

/*
 * 删除某个tag
 * */
const handleClose = (removedTag) => {
  const updatedTags = tags.filter((tag) => tag !== removedTag);
  setTags(updatedTags);
  form.setFieldsValue({ [name]: updatedTags?.join(separator) });
};

编辑状态

当我们处于编辑状态的时候,打开表单后,它原本就有内容了

监听一下表单的内容,如果存在,则使用分隔标记分隔后塞入tags中

useEffect(() => {
    if (form.getFieldValue(name)) setTags(form.getFieldValue(name).split(separator));
  }, [form.getFieldValue(name)]);

Antd4.x完整代码

折叠源码文章来源地址https://www.toymoban.com/news/detail-690941.html

import React, { memo, useEffect, useRef, useState } from 'react';
import { Input, message, Tag, Tooltip } from 'antd';
import PropTypes from 'prop-types';
import { PlusOutlined } from '@ant-design/icons';

/*
 * tag形式分隔
 * */
const TagInput = memo((props) => {
  const [tags, setTags] = useState([]); // 待分隔列表
  const [inputVisible, setInputVisible] = useState(false); // 是否显示输入框
  const [inputValue, setInputValue] = useState(''); // 输入框的值
  const { title = '新增一个', separator = ',', maxLength = 40, color = 'orange', form, name } = props;
  const saveInputRef = useRef();

  useEffect(() => {
    if (inputVisible) saveInputRef.current.focus();
  }, [inputVisible]);

  useEffect(() => {
    if (form.getFieldValue(name)) setTags(form.getFieldValue(name).split(separator));
  }, [form.getFieldValue(name)]);

  /*
   * 删除某个tag
   * */
  const handleClose = (removedTag) => {
    const updatedTags = tags.filter((tag) => tag !== removedTag);
    setTags(updatedTags);
    form.setFieldsValue({ [name]: updatedTags?.join(separator) });
  };

  /*
   * 新增一个tag
   * */
  const handleInputConfirm = () => {
    if (inputValue && tags.indexOf(inputValue) === -1) {
      const newTags = [...tags, inputValue];
      setTags(newTags);
      form.setFieldsValue({ [name]: newTags?.join(separator) });
    } else {
      message.error('请正确输入');
    }
    setInputVisible(false);
    setInputValue('');
  };

  return (
    <>
      {tags.map((tag) => {
        const isLongTag = tag.length > maxLength;
        const tagElem = (
          <Tag key={tag} closable onClose={() => handleClose(tag)} color={color}>
            {isLongTag ? `${tag.slice(0, 20)}...` : tag}
          </Tag>
        );
        return isLongTag ? (
          <Tooltip title={tag} key={tag}>
            {tagElem}
          </Tooltip>
        ) : (
          tagElem
        );
      })}
      {inputVisible && (
        <Input
          ref={saveInputRef}
          type="text"
          size="small"
          style={{ width: 78 }}
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
          onBlur={handleInputConfirm}
          onPressEnter={handleInputConfirm}
        />
      )}
      {!inputVisible && (
        <Tag onClick={() => setInputVisible(true)} style={{ background: '#fff', borderStyle: 'dashed' }}>
          <PlusOutlined /> {title}
        </Tag>
      )}
    </>
  );
});

TagInput.propTypes = {
  title: PropTypes.string, // 新增一个tag的标题
  separator: PropTypes.string, // 分隔符
  maxLength: PropTypes.number, // tag最大长度
  color: PropTypes.string, // tag颜色
  form: PropTypes.object, // form
  key: PropTypes.string, // form的key
};

export default TagInput;

Antd3.x完整代码

antd3.x中部分组件的用法不一样,需要修改一下

折叠源码

import React, { useEffect, useRef, useState } from 'react';
import { Icon, Input, message, Tag, Tooltip } from 'antd';
import PropTypes from 'prop-types';

/*
 * tag形式分隔
 * */
const TagInput = React.forwardRef((props, ref) => {
  const [tags, setTags] = useState([]); // 待分隔列表
  const [inputVisible, setInputVisible] = useState(false); // 是否显示输入框
  const [inputValue, setInputValue] = useState(''); // 输入框的值
  const {
    title = '新增一个',
    separator = ',',
    maxLength = 40,
    color = 'orange',
    form,
    name,
  } = props;
  const saveInputRef = useRef();

  useEffect(() => {
    if (inputVisible) saveInputRef.current.focus();
  }, [inputVisible]);

  useEffect(() => {
    if (form.getFieldValue(name)) {
      setTags(form.getFieldValue(name).split(separator));
    }
  }, [form.getFieldValue(name)]);

  /*
   * 删除某个tag
   * */
  const handleClose = (removedTag) => {
    const updatedTags = tags.filter((tag) => tag !== removedTag);
    setTags(updatedTags);
    form.setFieldsValue({ [name]: updatedTags?.join(separator) });
  };

  /*
   * 新增一个tag
   * */
  const handleInputConfirm = () => {
    if (inputValue && tags.indexOf(inputValue) === -1) {
      const newTags = [...tags, inputValue];
      setTags(newTags);
      form.setFieldsValue({ [name]: newTags?.join(separator) });
    } else {
      message.error('请正确输入');
    }
    setInputVisible(false);
    setInputValue('');
  };

  return (
    <>
      {tags.map((tag) => {
        const isLongTag = tag.length > maxLength;
        const tagElem = (
          <Tag
            key={tag}
            closable
            onClose={() => handleClose(tag)}
            color={color}
          >
            {isLongTag ? `${tag.slice(0, 20)}...` : tag}
          </Tag>
        );
        return isLongTag ? (
          <Tooltip title={tag} key={tag}>
            {tagElem}
          </Tooltip>
        ) : (
          tagElem
        );
      })}
      {inputVisible && (
        <Input
          ref={saveInputRef}
          type="text"
          size="small"
          style={{ width: 78 }}
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
          onBlur={handleInputConfirm}
          onPressEnter={handleInputConfirm}
        />
      )}
      {!inputVisible && (
        <Tag
          onClick={() => setInputVisible(true)}
          style={{ background: '#fff', borderStyle: 'dashed' }}
        >
          <Icon type="plus-circle" /> {title}
        </Tag>
      )}
    </>
  );
});

TagInput.propTypes = {
  title: PropTypes.string, // 新增一个tag的标题
  separator: PropTypes.string, // 分隔符
  maxLength: PropTypes.number, // tag最大长度
  color: PropTypes.string, // tag颜色
  form: PropTypes.object, // form
  key: PropTypes.string, // form的key
};

export default TagInput;

到了这里,关于React动态添加标签组件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端react入门day03-react获取dom与组件通信

    (创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 受控表单绑定  React中获取DOM 组件通信 父传子  父传子-基础实现 父传子-props说明 父传子 - 特殊的prop children 子传父  使用状态提升实现兄弟组件通信 使用Context机制跨

    2024年02月01日
    浏览(55)
  • 前端react入门day02-React中的事件绑定与组件

    (创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 React中的事件绑定 React 基础事件绑定 使用事件对象参数  传递自定义参数  同时传递事件对象和自定义参数  React中的组件  组件是什么 React组件 useState  修改状态的规

    2024年02月06日
    浏览(86)
  • 从javascript到vue再到react:前端开发框架的演变

    目录 JavaScript: 动态语言的基础 JavaScript:Web开发的起点 Vue.js: 渐进式框架的兴起 Vue.js:简洁、高效的前端框架 React.js: 声明式UI的革新 React.js:强大、灵活的前端框架 演变之路与未来展望 演变过程 当提到前端开发中的框架时,JavaScript、Vue.js和React.js是三个最常见的名词。它

    2024年02月07日
    浏览(53)
  • 前端开发笔记 | React Hooks子组件和父组件交互

    前端开发框架目前比较常用的就是react、vue等,其中使用React Hooks 带来了不少的好处,今天来聊聊React Hooks开发方式下,子组件和父组件的交互。 子组件定义 父组件调用子组件 父组件定义 子组件中刷新父组件按钮文案 实际效果:点击子组件中“改变父组件按钮”,父组件中

    2024年02月11日
    浏览(83)
  • 【前端知识】React 基础巩固(十七)——组件化开发(一)

    什么是组件化开发? 分而治之的思想 将一个页面拆分成一个个小的功能块 将应用抽象成一颗组件树 React的组件相对于Vue更加的灵活和多样 按照不同的方式可以分为很多类组件 根据 组件的定义方式 ,分为: 函数组件 、 类组件 根据 组件内部是否有状态需要维护 ,分为:

    2024年02月12日
    浏览(69)
  • 【前端知识】React 基础巩固(十九)——组件化开发(三)

    Main.jsx TabControl/index.jsx TabControl/style.css

    2024年02月13日
    浏览(55)
  • 前端(十五)——开源一个用react封装的图片预览组件

    👵博主:小猫娃来啦 👵文章核心:开源一个react封装的图片预览组件 Gitee:点此跳转下载 CSDN:点此跳转下载 装依赖 运行 打开 创建一个React函数组件并命名为 ImageGallery 。 在组件内部,使用useState钩子来定义状态变量,并初始化为合适的初始值。 selectedImageUrl 来追踪当前选

    2024年02月10日
    浏览(56)
  • JavaScript框架 Angular、React、Vue.js 的全栈解决方案比较

    在 Web 开发领域,JavaScript 提供大量技术栈可供选择。其中最典型的三套组合,分别是 MERN、MEAN 和 MEVN。前端框架(React、Angular 和 Vue)进行简化比较。 MERN 技术栈包含四大具体组件: MongoDB:一款强大的 NoSQL 数据库,以灵活的 JSON 格式存储数据。 Express.js:一套极简但强大的

    2024年02月03日
    浏览(58)
  • Three.js 的组件库react-three-fiber和react-three-drei

    类似于这种字体,可以用Text或者Text3d,但是要处理一个问题,就是要保证字体一直正面视角。 这中间的处理比较的麻烦,于是可以使用react-three-drei中的Html来做这件事

    2024年01月24日
    浏览(63)
  • react结合antd的Table组件实现动态单元格合并

    首先看一下antd的Table表单组件,合并单元格,用到了rowSpan(合并行)和colSpan(合并列)  后台返回的数据 我们希望把category的值相同的,行合并成一个单元格 类似于这种  rowSpan这个属性可以指定合并行。例如说第一行,指定rowSpan为3,意思就是合并三行,则后面紧挨的两行的ro

    2024年02月12日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包