I'm using the Headlessui combobox component but I had a problem when using it with Yup. The value I select with the combobox is written to the selectedMemory
state variable without any problem, but Yup still throws a required
error message. There must be a point I'm missing.
types for form
// form values for Yup
type RewardCreateFormValues = {
name: string
price: number
quantity: number
reward: string
}
type Reward = {
id: string
name: string
price: number
quantity: number
}
const rewards: Reward[] = [
{
id: "1",
name: "Reward 1",
price: 100,
quantity: 100
},
{
id: "2",
name: "Reward 2",
price: 200,
quantity: 200
},
{
id: "3",
name: "Reward 3",
price: 300,
quantity: 300
}
]
Created states and memoized filtered reward for combobox
const [selectedReward, setSelectedReward] = useState(rewards[0])
const [query, setQuery] = useState("")
// it filters the rewards according to the query
let filteredReward =
query === ""
? rewards
: rewards.filter(reward => {
return reward.name.toLowerCase().includes(query.toLowerCase())
})
const rewardOptions = useMemo(() => {
return filteredReward.map((reward: Reward) => ({
name: reward.name,
id: reward.id
}))
}, [filteredReward])
object validation with Yup and form initial values
const validationSchema = Yup.object().shape({
name: Yup.string().required("Required"),
price: Yup.number().required("Required"),
quantity: Yup.number().required("Required"),
reward: Yup.string().required("Required")
})
const initialValues: RewardCreateFormValues = {
name: "",
price: 0,
quantity: 0,
reward: ""
}
And combobox;
<GridItem colSpan={3}>
<FormControl>
<Box>
<FormLabel>Select a Reward</FormLabel>
<Combobox as="div" value={selectedReward} onChange={setSelectedReward}>
<Combobox.Button as="div">
<Combobox.Input
className="w-full rounded-md border-0 bg-white py-2 pl-3 pr-12 text-gray-900 shadow-sm ring-1 ring-inset ring-[#E2E8F0] focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
name="reward"
onChange={(event: any) => setQuery(event.target.value)}
placeholder="Select a Reward"
displayValue={(selectedReward: Reward) =>
selectedReward ? selectedReward.name : ""
}
/>
</Combobox.Button>
<Combobox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{rewardOptions.map(reward => (
<Combobox.Option
key={reward.id}
value={reward}
className={({ active }) =>
classNames(
"relative cursor-default select-none py-2 pl-3 pr-9",
active ? "bg-indigo-600 text-white" : "text-gray-900"
)
}
>
{({ active, selected }) => (
<>
<Box display={"flex"}>
<Text
as={"span"}
className={classNames(
"truncate",
selected ? "font-semibold" : "font-normal"
)}
>
{reward.name}
</Text>
</Box>
{selected && (
<Text
as={"span"}
className={classNames(
"absolute inset-y-0 right-0 flex items-center pr-4",
active ? "text-white" : "text-indigo-600"
)}
>
<CheckIcon
className="h-5 w-5"
aria-hidden="true"
/>
</Text>
)}
</>
)}
</Combobox.Option>
))}
<Combobox.Option
value={""}
className={
"relative cursor-default select-none py-2 pl-3 pr-9 bg-indigo-100 hover:bg-indigo-600 hover:text-white text-gray-900"
}
onClick={onOpen}
>
<Box
display={"flex"}
flexDirection={"row"}
gap={2}
alignItems={"center"}
>
<PlusIcon className="h-5 w-5" aria-hidden="true" />
<button>Create a Reward</button>
</Box>
</Combobox.Option>
</Combobox.Options>
</Combobox>
</Box>
<ErrorMessage name="reward" component="p" className="mt-2 text-sm text-red-600" />
</FormControl>
</GridItem>
<GridItem colSpan={3}>
<Box display={"flex"} justifyContent="flex-end">
<Button
isLoading={isSubmitting}
isDisabled={isSubmitting}
type="submit"
colorScheme="indigo"
variant="solid"
>
Create Listing
</Button>
</Box>
</GridItem>
As I mentioned above, the problem is that Yup keeps throwing the required error even if the selectedMemory
content and value change.
Headless/ui documentation is really bad. So I did some experimental development myself.
I was using yup but I was keeping seletedReward in a state, I removed the state there and instead I added values, setFieldValue values to the field in formic (outside of the code I shared) and I started to do the control with formic + yup and I updated the props accordingly.
I started to get the reward as an object in yup, not a string, for this I updated the validationSchema, formValues content.
After doing these, it started to work smoothly, I hope it helped your problem. If you have a similar problem, I would like to help if you share it as a comment.