React 16.x折騰記 - (8) 基於React+Antd封裝選擇單個文章分類(從構建到獲取)

前言

隨着管理的文章數量增多,默認的幾個分類知足不了現狀了,趁着重構的過程把相關的功能考慮進去javascript

原本想本身從頭寫過一個,看了下Antd有內置該類型的控件了,就不必本身造了java

通常本身寫,確定優先考慮數組對象格式[{tagName:'a',value:1}];react

Antd提供的是純數組,[string,string],那如何不改變它提供的格式狀況下拿到咱們想要的!git

拓展部分咱們須要的東東,有興趣的瞧瞧,沒興趣的止步..github

效果圖

需求分析及思路

需求梳理數組

  • 從接口拿到tags數組且構建枚舉對象,tags支持刪除添加 ,
  • 高亮tag,追加刪除的狀況要考慮進去;
  • 第一個爲默認分類,不容許刪除
  • 高亮顏色支持傳入
  • 標籤文字過長,則截斷,用氣泡懸浮來展現徹底的文本
  • 不容許添加一樣的(阻止並給予反饋)
  • 默認值初始化而且回饋,把值丟給父

實現antd

  • dvaeffect維護接口數據的獲取
  • 子組件除了暴露返回值,不作任何涉及Dva這類不純的東西,一切靠props丟進去

代碼實現

在引用處的父組件構建數據獲取,主要構建兩個,一個待渲染的數組,一個是枚舉(其實就是key-value映射);ide

由於要考慮和之前的版本兼容,全部一些固定的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();
  };
  // 記錄控件的ref
  input = React.createRef();

  // 顯示input後,直接聚焦
  showInput = () => {
    this.setState({ inputVisible: true }, () => this.input.current.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);
    }
  };

  render() {
    const { tags, inputVisible, inputValue, hightlightIndeX } = this.state;
    const { plusBtnText, activeColor } = this.props;
    return (
      <div>
        {tags.map((tag, index) => {
          const isLongTag = tag.length > 10;
          const tagElem = (
            <Tag
              key={tag}
              color={hightlightIndeX === index ? (activeColor ? activeColor : '#40a9ff') : ''}
              closable={index !== 0}
              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.input}
            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>
    );
  }
}



複製代碼

用法

寫成受控組件,無數據不渲染flex

props 解釋 格式類型 是否可選
data 待遍歷的數組 數組 必選
onChange 選中的回調 函數 必選
addTag 添加標籤的回調 函數 必選
remvoeTag 移除標籤的回調 函數 必選
defaultValue 默認值 字符串 可選
plusBtnText 追加按鈕文本替換 字符串 可選
activeColor 高亮的顏色 字符串 可選
{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="草稿"
              activeColor="#108ee9"
              plusBtnText="新的分類"
            />
          </Row>
        ) : null}

複製代碼

總結

不對之處請留言,會及時修正.謝謝閱讀.

相關文章
相關標籤/搜索