React 折腾记 - (8) 基于React+Antd封装选择单个文章分类(从构建到获取)

栏目: 服务器 · 发布时间: 6年前

内容简介:随着管理的文章数量增多,默认的几个分类满足不了现状了...趁着重构的过程把相关的功能考虑进去本来想自己从头写过一个,看了下

随着管理的文章数量增多,默认的几个分类满足不了现状了...

趁着重构的过程把相关的功能考虑进去

本来想自己从头写过一个,看了下 Antd 有内置该类型的控件了,就没必要自己造了

一般自己写,肯定优先考虑数组对象格式 [{tagName:'a',value:1}] ;

Antd 提供的是纯数组, [string,string] ,那如何不改变它提供的格式情况下拿到我们想要的!

拓展部分我们需要的东东,有兴趣的瞧瞧,没兴趣的止步..

效果图

React 折腾记 - (8) 基于React+Antd封装选择单个文章分类(从构建到获取)

需求分析及思路

需求梳理

  • 从接口拿到 tags 数组, tags 支持删除添加
  • 高亮 tag ,追加删除的情况要考虑进去(删除要考虑进去);
  • 第一个为默认分类,不允许删除
  • 标签文字过长,则截断,用气泡悬浮来展示完全的文本
  • 不允许添加同样的(阻止并给予反馈)
  • 默认值初始化并且回馈
  • 把值丢给父

实现

  • dvaeffect 维护接口数据的获取
  • 子组件除了暴露返回值,不做任何涉及 Dva 这类不纯的东西,一切靠 props 丢进去

代码实现

在引用处的父组件构建数据获取,主要构建两个,一个待渲染的数组,一个是 枚举 (其实就是 key-value 映射);

因为要考虑和以前的版本兼容,所有一些固定的 key-value ,还有默认值也要考虑进去(请求失败的时候)

DocumentType.js

/*
 * @Author: CRPER
 * @LastEditors: CRPER
 * @Github: https://github.com/crper
 * @Motto: 折腾是一种乐趣,求知是一种追求。不懂就学,懂则分享。
 * @Description: 文档类型维护
 */
import React, { PureComponent } from 'react';
import { Tag, Input, Tooltip, Icon, message } from 'antd';

// 对象深比较
import isEqual from 'lodash/isEqual';

export default class DocumentType extends PureComponent {
  static getDerivedStateFromProps(nextProps, prevState) {
    if (isEqual(nextProps.data, prevState.prevData)) {
      return null;
    }
    if (nextProps.data) {
      return {
        defaultValue: nextProps.defaultValue ? nextProps.defaultValue : null,
        tags: nextProps.data,
        prevData: nextProps.data,
      };
    } else {
      return null;
    }
  }
  state = {
    tags: [], // 标签列表
    hightlightIndeX: 0, // 若是外部没有
    inputVisible: false, // 输入框默认隐藏
    inputValue: '', // 输入框默认值
  };

  //获取默认值
  initDefaultValue = () => {
    const { defaultValue, hightlightIndeX, tags } = this.state;
    // 若是有,则取遍历取得;若是外部没有传入默认值则取数组第一位
    if (defaultValue) {
      let index = tags.indexOf(defaultValue);
      // 若是传入的默认值不存在,则默认取下标为0的
      index = index === -1 ? 0 : index;
      this.setState({ hightlightIndeX: index }, () => {
        this.props.onChange(this.getTagValueFromIndex(index));
      });
    } else {
      this.props.onChange(this.getTagValueFromIndex(hightlightIndeX));
    }
  };

  componentDidMount = () => {
    this.initDefaultValue();
  };

  // 显示input后,直接聚焦
  showInput = () => {
    this.setState({ inputVisible: true }, () => this.input.focus());
  };

  // 保存input输入的值
  handleInputChange = e => {
    this.setState({ inputValue: e.target.value });
  };

  // 新增判定
  handleInputConfirm = () => {
    const { inputValue, tags: prevTags, defaultValue } = this.state;

    // 若是输入的值已经存在或空值,则不添加
    if (inputValue === defaultValue) {
      message.error('已存在同样的类型!!!');
      this.setState({ inputValue: '' });
      this.input.focus();
      return false;
    }
    if (!inputValue) {
      this.setState({ inputVisible: false, inputValue: '' });
      return false;
    }

    let tags = prevTags;
    if (inputValue && tags.indexOf(inputValue) === -1) {
      tags = [...tags, inputValue];
    }
    this.setState({
      tags,
      inputVisible: false,
      inputValue: '',
    });

    // 传递给父的新增标签回调
    if (this.props.addTag) {
      this.props.addTag(inputValue);
    }
  };

  // 取得对应index下的tag的值
  getTagValueFromIndex = index => {
    const { tags } = this.state;
    return tags[index];
  };

  // 高亮TAG
  hightlightTag = index => {
    this.setState({ hightlightIndeX: index });
    if (this.props.onChange) {
      this.props.onChange(this.getTagValueFromIndex(index));
    }
  };

  // 删除tag
  handleClose = removeTag => {
    const { hightlightIndeX, tags } = this.state;
    if (this.props.removeTag) {
      this.props.removeTag(removeTag);
    }
    // 若是删除的位置和高亮的位置同一个,则高亮往前一位
    if (tags.indexOf(removeTag) === tags.length - 1) {
      this.hightlightTag(hightlightIndeX - 1);
    }
  };

  // 记录控件的ref
  saveInputRef = input => (this.input = input);

  render() {
    const { tags, inputVisible, inputValue, hightlightIndeX } = this.state;
    const { plusBtnText } = this.props;
    return (
      <div>
        {tags.map((tag, index) => {
          const isLongTag = tag.length > 10;
          const tagElem = (
            <Tag
              key={tag}
              closable={index !== 0}
              style={hightlightIndeX === index ? { color: '#fff', background: '#108ee9' } : {}}
              onClick={() => this.hightlightTag(index)}
              afterClose={() => this.handleClose(tag)}
            >
              {isLongTag ? `${tag.slice(0, 10)}...` : tag}
            </Tag>
          );
          return isLongTag ? (
            <Tooltip title={tag} key={tag}>
              {tagElem}
            </Tooltip>
          ) : (
            tagElem
          );
        })}
        {inputVisible && (
          <Input
            ref={this.saveInputRef}
            type="text"
            size="small"
            style={{ width: 78 }}
            value={inputValue}
            onChange={this.handleInputChange}
            onBlur={this.handleInputConfirm}
            onPressEnter={this.handleInputConfirm}
          />
        )}
        {!inputVisible && (
          <Tag onClick={this.showInput} style={{ background: '#fff', borderStyle: 'dashed' }}>
            <Icon type="plus" /> {plusBtnText ? plusBtnText : 'New Tag'}
          </Tag>
        )}
      </div>
    );
  }
}

复制代码

用法

写成受控组件,无数据不渲染

{typeNames && typeNames.length > 0 ? (
          <Row type="flex" justify="start" align="middle">
            <span style={{ fontSize: 16, fontWeight: 700 }}>文章类型</span>
            <Divider type="vertical" />
            <DocumentType
              data={typeNames}
              onChange={this.getTagValue}
              addTag={this.addTag}
              removeTag={this.removeTag}
              defaultValue="草稿"
              plusBtnText="新的分类"
            />
          </Row>
        ) : null}

复制代码
props 解释 格式类型
data 待遍历的数组 数组
onChange 选中的回调 函数
addTag 添加标签的回调 函数
remvoeTag 移除标签的回调 函数
defaultValue 默认值 字符串
plusBtnText 追加按钮文本替换 字符串

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

你的品牌,价值千万

你的品牌,价值千万

温迪 / 人民邮电出版社 / 2018-7-1 / 49.00元

“大道无术,万法归心。” 不管是互联网、社交媒体,还是 AI 怎样让人眼花缭乱。从“真心”出发塑造的个人品牌,都将带你从容面对任何一种变化的冲击。现代生活变得越来越透明,如果你不懂得如何真实、精准地定位和呈现自己,你的个人品牌在 碎片信息中被误解、被曲解就是一种必然。 本书分四步引导你剖析自己、发现自我,构建可持续的品牌生态系统,策划品牌战略,提升个人呈现力,并在最后带你勾画出一幅完整的个人......一起来看看 《你的品牌,价值千万》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具