1. 程式人生 > >React 折騰記 - (8) 基於React+Antd封裝選擇單個文章分類(從構建到獲取)

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

前言

隨著管理的文章數量增多,預設的幾個分類滿足不了現狀了...

趁著重構的過程把相關的功能考慮進去

本來想自己從頭寫過一個,看了下Antd有內建該型別的控制元件了,就沒必要自己造了

一般自己寫,肯定優先考慮陣列物件格式[{tagName:'a',value:1}];

Antd提供的是純陣列,[string,string],那如何不改變它提供的格式情況下拿到我們想要的!

拓展部分我們需要的東東,有興趣的瞧瞧,沒興趣的止步..


效果圖


需求分析及思路

需求梳理

  • 從介面拿到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 追加按鈕文字替換 字串

總結

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