I have been trying to make a dropdown component that is reusable and decided to put 2 inputs in there so I can validate the range of value. The validation happens but the state passed as a prop does not update before setIsOpen function is fired.
// states
const { data, error, isLoading } = useRegionsData();
const {
filters,
setFilters,
} = useFilter();
const [localFilters, setLocalFilters] = useState<FilterValues>({
price: { min: '', max: '' },
region: [],
area: { min: '', max: '' },
bedroom: '',
});
const [localErrors, setLocalErrors] = useState<FilterErrors>({
area: false,
bedroom: false,
price: false,
});
console.log(localFilters.price);
console.log(localErrors);
/* this useffect initilizes local filter regions to look like a object {id,name,checked} */
useEffect(() => {
const savedFilters = localStorage.getItem('filters');
if (savedFilters) {
const filterData = JSON.parse(savedFilters) as FilterValues;
setLocalFilters(filterData);
} else if (data) {
const regionData = data.map((region) => ({
id: region.id,
name: region.name,
checked: false,
}));
setLocalFilters((prevState) => ({
...prevState,
region: regionData,
}));
}
}, [data, setLocalFilters]);
// update functions
const handlePriceConfirmClick = () => {
if (!localFilters.price.min || !localFilters.price.max) {
setLocalErrors((prevErrors) => ({
...prevErrors,
price: true,
}));
return;
}
if (Number(localFilters.price.min) > Number(localFilters.price.max)) {
setLocalErrors((prevErrors) => ({
...prevErrors,
price: true,
}));
return;
}
// If everything is okay, submit the form
setFilters(localFilters);
localStorage.setItem('filters', JSON.stringify(localFilters));
};
const handlePriceChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setLocalFilters((prevState) => {
const updatedPrice = {
...prevState.price,
[name]: value,
};
const minPrice = Number(updatedPrice.min);
const maxPrice = Number(updatedPrice.max);
const hasError = minPrice >= maxPrice;
setLocalErrors((prevErrors) => ({
...prevErrors,
price: hasError,
}));
return {
...prevState,
price: updatedPrice,
};
});
};
so what i am interested in is NewDropdown component when handleSubmit happends the error should stop the function from compleating but for some reason the previous value of state is passed to it.
// dropdown component where error is suppose to decide if it closes it
import { useEffect, useRef, useState } from 'react';
import upIcon from '../../assets/images/DropdownIconUp.svg';
import downIcon from '../../assets/images/DropdownIconDown.svg';
import Button from '../button/Button';
type Props = {
children: React.ReactNode;
buttonText: string;
dropdownTitle: string;
onSubmit: () => void;
error?: boolean;
};
const NewDropdown = ({
children,
dropdownTitle,
buttonText,
onSubmit,
error = false,
}: Props) => {
const [isOpen, setIsOpen] = useState(false);
const buttonRef = useRef<HTMLButtonElement>(null);
const dropdownRef = useRef<HTMLDivElement>(null);
/* content outside of dropdown will trigger setIsOpen(false) */
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
buttonRef.current &&
!buttonRef.current.contains(event.target as Node) &&
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
const handleClick = () => {
setIsOpen((prevState) => !prevState);
};
const handleSubmit = () => {
console.log('err', error);
onSubmit();
if (!error) {
setIsOpen(false);
}
};
return (
<div className='relative'>
<button
ref={buttonRef}
onClick={handleClick}
className={`rounded-md py-2 px-[14px] flex justify-center items-center gap-1 text-text font-medium ${
isOpen ? 'bg-selected' : 'bg-transparent'
}`}
>
{buttonText}
<span>
<img
src={isOpen ? upIcon : downIcon}
alt={isOpen ? 'menu open icon' : 'menu closed icon'}
/>
</span>
</button>
{isOpen && (
<div
ref={dropdownRef}
className='absolute bottom-0 transform translate-y-full left-0 p-6 rounded-[10px] border border-border bg-white z-20'
>
<p className='font-firago font-medium mb-6'>{dropdownTitle}</p>
{children}
<div className='flex justify-end mt-[32px]'>
<Button
className='bg-primary text-white py-2 px-[14px] rounded-lg font-firago font-medium transition-colors duration-200 ease-linear hover:bg-primaryHover'
onClick={handleSubmit}
>
არჩევა
</Button>
</div>
</div>
)}
</div>
);
};
export default NewDropdown;
My guess was that the state was old so I used let variable instead but with that the error does not update at all because it is not a react state