如何使用ReactJS向react-select下拉组件动态新增选项
问题描述
我有一个基于react-select依赖提供的Select组件实现的下拉组件,在下拉选项列表中添加了一个按钮,需求为点击该按钮后可输入文本,按下回车键时将输入的文本添加到下拉选项列表中。曾尝试将初始选项列表存储在state变量中后执行更新操作,但未达到预期效果,组件实现代码如下:
import React from "react"; import PropTypes from "prop-types"; import Select from "react-select"; import { get } from "../../helpers"; const ReactSelect = props => { const { state, component, onChange, disabled = false } = props; let showError = false; const { name, placeholder, optionConfig = false, optionList = [], className, maxItems = null } = component; const newOptionList=[ ...optionList, {label: ( <> <hr /> <button className="btn btn-icon" > <cds-icon shape="plus"></cds-icon>ADD TYPE </button> </> ), value: "Add Type"} ]; const options = !!optionConfig ? get(state, `${optionConfig}`, []) : newOptionList; const getSelectValue = () => { const selectedList = get(state, `${name}`, []); const selectedSet = new Set(selectedList); return options.filter(item => selectedSet.has(item.value)); }; const handleOnChange = selectedOption => { console.log(selectedOption); if(selectedOption.value !== "Add Type"){ console.log(selectedOption); onChange({ value: selectedOption, component }); } else { console.log("button selected"); onChange({ value: selectedOption, component }); } }; const defaultSelectProps = { value: getSelectValue(), name: "category", options, menuPosition: "absolute", isDisabled: disabled, placeholder }; return ( <div key={name}> <div className={`react-select-box ${className}`}> <Select {...defaultSelectProps} /> { showError && <span className="clr-subtext multiselect-error"> You can select upto {maxItems} items only. </span> } </div> </div> ); }; ReactSelect.propTypes = { selectWithoutBox: PropTypes.bool }; ReactSelect.defaultProps = { selectWithoutBox: false }; export default ReactSelect;
上述代码中,getSelectValue函数负责返回用户选中的选项并完成展示,引入的get工具方法实现如下:
export function get(source, path, defaultValue) { if (typeof path === "number") { path = String(path); } if (path && source) { let parts = path.split("."); let length = parts.length; let result = source; for (let i = 0; i < length; i++) { let item = result[parts[i]]; if (item === null || item === undefined) { return item || defaultValue; } result = item; } return result; } return defaultValue; }
需要修改代码实现向下拉组件选项列表中动态添加新增值的功能。
问题原因
- 选项列表每次渲染都直接基于
component.optionList临时拼接,没有独立状态存储动态新增的选项,新增值无法持久保留 - 自定义的"ADD TYPE"按钮没有拦截默认选中事件,点击后会被当做普通选项提交,没有触发输入流程
- 没有对接react-select的创建选项能力,回车输入的文本无法被识别为新选项加入列表
修改方案
直接使用react-select官方提供的CreatableSelect组件实现创建选项能力,搭配本地状态存储动态新增的选项,同时给自定义添加按钮绑定事件拦截默认行为、触发输入框聚焦,修改后的完整代码如下:
import React, { useState, useRef } from "react"; import PropTypes from "prop-types"; import CreatableSelect from "react-select/creatable"; import { get } from "../../helpers"; const ReactSelect = props => { const { state, component, onChange, disabled = false } = props; const selectRef = useRef(null); let showError = false; const { name, placeholder, optionConfig = false, optionList = [], className, maxItems = null } = component; // 本地存储动态新增的选项,初始值为传入的默认选项 const [customOptions, setCustomOptions] = useState(optionList); // 拼接最终选项列表,尾部固定添加操作按钮,标记isDisabled防止被当成普通选项选中 const finalOptions = [ ...(!!optionConfig ? get(state, `${optionConfig}`, []) : customOptions), { label: ( <> <hr /> <button className="btn btn-icon" onClick={(e) => { // 阻止事件冒泡,防止触发选项选中 e.stopPropagation(); e.preventDefault(); // 聚焦到select输入框,进入输入态 selectRef.current?.focus(); }} > <cds-icon shape="plus"></cds-icon>ADD TYPE </button> </> ), value: "Add Type", isDisabled: true } ]; const getSelectValue = () => { const selectedList = get(state, `${name}`, []); const selectedSet = new Set(selectedList); return finalOptions.filter(item => selectedSet.has(item.value)); }; // 处理回车创建新选项的逻辑 const handleCreateOption = (inputValue) => { // 去重判断 const isExist = finalOptions.some(opt => opt.value === inputValue); if (isExist) return; const newOption = { label: inputValue, value: inputValue }; // 更新本地选项列表 setCustomOptions(prev => [...prev, newOption]); // 自动选中新创建的选项 const currentSelected = getSelectValue(); onChange({ value: [...currentSelected, newOption], component }); }; const handleOnChange = selectedOption => { onChange({ value: selectedOption, component }); }; const defaultSelectProps = { ref: selectRef, value: getSelectValue(), name: "category", options: finalOptions, menuPosition: "absolute", isDisabled: disabled, placeholder, onCreateOption: handleCreateOption, isMulti: true // 单选场景可删除该配置 }; return ( <div key={name}> <div className={`react-select-box ${className}`}> <CreatableSelect {...defaultSelectProps} /> { showError && <span className="clr-subtext multiselect-error"> You can select upto {maxItems} items only. </span> } </div> </div> ); }; ReactSelect.propTypes = { selectWithoutBox: PropTypes.bool }; ReactSelect.defaultProps = { selectWithoutBox: false }; export default ReactSelect;
关键修改点说明
- 引入
CreatableSelect替换原生Select,自带输入创建选项的能力,不需要额外实现输入框 - 新增
customOptions本地状态,存储动态新增的选项,解决之前选项每次渲染重置的问题 - 给尾部的"ADD TYPE"选项加
isDisabled: true标记,防止被当做普通选项选中,同时给按钮绑定点击事件,拦截默认行为后触发输入框聚焦 - 实现
onCreateOption回调,处理回车创建新选项的逻辑,做去重校验后更新选项列表,自动选中新添加的选项
内容的提问来源于stack exchange,提问作者Rimmi
相关产品推荐
相关产品推荐

