I have a function that filters out the data onChange events. It working fine as far as the filtration goes, But the issue is with the onChange event and setState
I'm using React Select to give the value onChange:
<Select
id="status"
isClearable={false}
options={statusOptions}
defaultValue={statusOptions[0]}
onChange={(choice) => handleStatusFilter(choice.value)}
/>
Here the statusOptions are :
const statusOptions = [
{ value: '', label: 'Clear' },
{ value: 'draft', label: 'Draft' },
{ value: 'ready', label: 'Ready' },
{ value: 'expired', label: 'Expired' },
]
Now onChange event I'm calling the handleStatusFilter
:
// state
const [status, setStatus] = useState('')
// ** handleStatusFilter
// ** Function to handle status filter
const handleStatusFilter = (value) => {
let updatedData = []
const dataToFilter = () => {
if (title.length || type.length || createdAt.length || createdBy.length || status.length) {
return filteredData
} else {
return results
}
}
setStatus(value)
if (value.length) {
updatedData = dataToFilter().filter((item) => {
const startsWith = item.status.toLowerCase().startsWith(value.toLowerCase())
const includes = item.status.toLowerCase().includes(value.toLowerCase())
if (startsWith) {
return startsWith
} else if (!startsWith && includes) {
return includes
} else return null
})
setFilteredData([...updatedData])
setStatus(value)
}
}
So it works the first time when the user selects any option, but not on changing his selection after that. For example;
data gets filtered when the user selects the draft
option. But when directly he goes from draft
to ready
it doesn't work, and if the user clears the state hitting the Clear
option from statusOptions
, and then the user selects ready
then data gets filtered with this value properly.
I'm assuming there's should be a way to clear the value onChange instead of Clearing manually selecting the empty string. So how to counter this problem, is there any better approach to follow, instead of the setState method.
CodePudding user response:
The problem is more or less with this condition here:
const dataToFilter = () => {
if (title.length || type.length || createdAt.length || createdBy.length || status.length) {
return filteredData
} else {
return results
}
}
Probably it returns results
on the first selection because all values in the condition are falsy. For sure status.length
is 0
and therefore falsy
. From then on it allways returns filteredData
, since status.length
is truthy. Obviosly the filtered values do not include the other values anymore. So whatever you select, you will allways filter the filteredData again.
Instead you have to filter the original value results
on each selection.
updatedData = results.filter( // here use results instead of filtered values.
(item) => {
const startsWith = item.status.toLowerCase().startsWith(value.toLowerCase())
const includes = item.status.toLowerCase().includes(value.toLowerCase())
/** besides that, this can be simplified
if (startsWith) {
return startsWith
} else if (!startsWith && includes) {
return includes
} else return null
})**/
return startsWith || includes
Edit1: What I said above is not wrong, but it does not consider the other filters. So in general you are restricting your results so that they can not be filtered any further. Instead, you have to apply all the filters in the same filter function, to filter down the values. Here is a codesandbox with a full example.
I used a useEffect for that and a simple html select for demonstration. But you can easiely apply that to your code.