I'm building a multiselect dropdown with a field to search for options. It's working perfectly except for one aspect:
1 - Here I just selected the options. The first input shows the state of the variable:
2 - Here I searched for values with the digit "4". The option "4" is already selected, but I didn't do this, I selected options "1" and "3":
Here's is the components' code (I removed the parts that doesn't matter about this issue like tailwind notations and functions that is not used by the filter logic):
"use client"
import React, { useEffect, useId, useState } from "react"
export default function Cscheckdropdown(props: Props) {
const inputText = useId()
const inputSearch = useId()
const [value, setValue] = useState < string[] > ([])
const [searchValue, setSearchValue] = useState("")
const [optionsFiltered, setOptionsFiltered] = useState(props.options.map((option) => { return option }))
const handleSetValue = (e: React.MouseEvent<HTMLInputElement>) => {
const valor = e.currentTarget.value
if (!value.includes(valor)) {
setValue([...value, valor])
}
else if (value.includes(valor)) {
setValue(value.filter(v => v != valor))
}
}
useEffect(() => {
setOptionsFiltered(
props.options.filter((x) => {
if (x.includes((searchValue))) {
return x
}
})
)
}, [props.options, searchValue])
return (<>
<div>
<input id={inputSearch} name={props.name + "_inputSearch"} type="text" onChange={(e) => { setSearchValue(e.currentTarget.value) }} value={searchValue} />
</div>
{
optionsFiltered.map((option, index) => {
return (
<div key={index}>
<label htmlFor={inputText + index}>
<input type="checkbox" id={inputText + index} name={props.name} value={option} onClick={handleSetValue} />
{option}
</label>
</div>
)
})
}
</>
)
}
Your checkbox element has no checked
attribute. I was expecting to see something like checked={value.includes(option)}
. It looks like you're relying on the DOM saving the selection; it needs to be state driven.
Further, option
makes a better key than than index
. That's probably why you're seeing the first option remaining selected, even after filtering.
Finally, your optionsFiltered
variable would be better written as a memo, rather than a state variable with an effect hook. i.e.
const optionsFiltered = useMemo(() => {
return props.options.filter((opt) => {
return opt.includes(searchValue);
});
}, [props.options, searchValue]);