So, I have a case where I want to show order of selection while selecting fields in ant design multi select component.
The idea is at first all the labels will be like Label, Label, Label, and so on. Let say I will select first label so expected output will be to show 1 along side Label "Label-1" and other labels will remain as it is. When I will select second label "2" has to be shown with the second label "Label-2" and when I will deselect first label it has to be reset to Label to "Label" and now second label has to change like Label-1 because that is now at first index in the selected array.
Why I want to do this? I actually want the user to see which item he has selected first, so to achieve that I want to show an order with that.
We pass options to the options prop in Select component and label and value prepares when we create a select component at first. So, if we want to change the label we must need to change the options prop values because on each selection or deselection labels are changing so I can handle by updating the options each time. But that seems a very bad solution as it will cost a lot on each selection and I wonder if it slows down my selection component as well as we have to compute a lot on each selection.
Yes you can, you'll need some tricks with useState
and useRef
. Also, if you need to show selection order inside field (not on drop down list) you'll need to render custom tags
. Here is a little example:
// ref to store current selected options
const selectedOptions = useRef<string[]>([])
// store your options in ref as they will not change
const optionsRef = useRef<SelectProps['options']>([{
label: 'label',
value: '1'
},{
label: 'label',
value: '2'
},{
label: 'label',
value: '3'
}]).current
// create a state to change the options based on selection order
const [options, setOptions] = useState<SelectProps['options']>(optionsRef)
function handleChange(selected: string[]) {
// update current selected options
selectedOptions.current = selected
// update state based on current selection order
setOptions(current => current.map(option => {
const indexOf = selected.indexOf(option.value)
// remove any order information, eg: _1 or _321 will be removed
// adjust this based on your ordering information
const label = option.label.replace(/_(.*)/, '')
return {
...option,
label: `${label}${indexOf < 0? '' : `_${indexOf + 1}`}`
}
}))
};
// render custom tags to show order without opening select list
function tagRender({
value, label, closable, onClose
}: CustomTagProps) {
const onPreventMouseDown = (event: React.MouseEvent<HTMLSpanElement>) => {
event.preventDefault();
event.stopPropagation();
};
// search for default option and then look for its index on selected options
const option = optionsRef.find(item => item.value === value)
const indexOf = selectedOptions.current.indexOf(option?.value)
return (
<Tag
onMouseDown={onPreventMouseDown}
closable={closable}
onClose={onClose}
style={{ marginRight: 3 }}
>
// always increase index in 1 so it will not start from 0
{`${label}_${indexOf + 1}`}
</Tag>
);
};
return (
<Select
mode="multiple"
allowClear
style={{ width: '100%' }}
placeholder="Please select"
onChange={handleChange}
options={options}
tagRender={tagRender}
/>
)
Here you can also find a working code sandbox example.
Hope this helps :)