I'm trying to build a form using React with select elements from react-select and the validation done using react-hook-form.
Upon change the value seems to successfully change, but when I submit the form the select value is undefined.
I have input fields and those work properly, but for my select and multi-select elements it fails.
New to React so very confused, tried to find an answer to this but not able to. Removed some of the code to keep the interesting parts, hopefully I got it right.
import React, { useState } from "react";
import Collapsible from "../collapsible/Collapsible"
import Select, { components } from 'react-select';
import axios from "axios";
import { useInput } from "../../hooks"
import { useForm } from 'react-hook-form';
import { selectStyles } from "./styles"
import allBots from "../../data/bots";
import platforms from "../../data/platforms";
import connectionTypes from "../../data/connection_types";
import generatePayload from "./generate_payload";
const Connect = () => {
const { setValue, register, handleSubmit, errors } = useForm();
const [values, setValues] = useState({
selectedOption: []
});
const CustomOption = props => {
return (
<components.Option {...props}>
<div style={{ display: "inline-block", "min-width": "40px" }}>{props.data.suffix}</div>
<div style={{ display: "inline-block" }}>{props.data.value}</div>
</components.Option>
);
};
const handleBotChange = selectedBot => {
setValue("bots", selectedBot);
setValues(selectedBot);
};
const handleMultiChange = selectedOption => {
console.log(selectedOption);
setValue("platform", selectedOption);
setValues(selectedOption);
};
const onSubmit = (data) => {
console.log(data);
// outputs { "bots": undefined, "platform": undefined, username: "theinput" }
}
return (
<Collapsible className="collapsible" header="Connect">
<div className="collapsibleContent">
<form onSubmit={handleSubmit(onSubmit)}>
<Select
className="reactSelect"
name="platform"
placeholder="Platform"
value={values.platform}
onChange={handleMultiChange}
options={platforms}
ref={e => register({ name: "platform", required: true })}
/>
<Select
isMulti
name="bots"
options={bots}
onChange={handleBotChange}
closeMenuOnSelect={false}
blurInputOnSelect={false}
className="basic-multi-select formItem"
classNamePrefix="select"
components={{Option: CustomOption}}
styles={selectStyles}
placeholder="Bots"
ref={e => register({ name: "bots", required: true })}
/>
<input
name="username"
ref={register({
validate: value => value !== "illegal_value" || "Illegal value"
})}
/>
{errors.username && errors.username.message}
<button type="submit">Submit</button>
</form>
</div>
</Collapsible>
);
}
export default Connect;
Update: I decided to use the "Controller" functionality of react-hook-form to integrate it with react-select. This is how part my code ended up. I also use SocketIO events in order to dynamically populate the options in the dropdown list. This solution isn't perfect but it's something.
import React, { useState, useEffect } from "react";
import Collapsible from "../collapsible/Collapsible"
import Select, { components } from 'react-select';
import { useForm, Controller } from 'react-hook-form';
import { selectStyles } from "./styles"
import connectionTypes from "../../data/connection_types";
import { OptionWithSuffix } from "../form_components/components";
import socket from "../socket";
const Connect = () => {
const { errors, control, watch, getValues, handleSubmit, register, setValue, reset } = useForm();
const watchFields = watch(["connectionType"]);
const [bots, setBots] = useState([]);
const connectionType = getValues("connectionType") || {};
useEffect(() => {
socket.on("bots", data => {
setBots(data.map(obj => ({...obj, suffix: obj.count, value: obj.name, label: obj.name})));
});
return () => {
socket.off("bots");
};
}, []);
const onSubmit = (data) => {
console.log(data);
}
return(
<Collapsible className="collapsible" header="Connect">
<div>
<form onSubmit={handleSubmit(data => onSubmit(data))}>
<Controller
control={control}
as={<Select className="formItem" options={platforms} />}
rules={{ required: true }}
placeholder="Select platform"
name="platform"
styles={selectStyles}
register={register}
setValue={setValue}
/>
<Controller
control={control}
as={<Select className="formItem" options={connectionTypes}/>}
rules={{ required: true}}
placeholder="Select connection type"
name="connectionType"
styles={selectStyles}
register={register}
setValue={setValue}
/>
{errors.connectionType && "Connection type is required"}
{connectionType.value === "specific" &&
<Controller
control={control}
as={
<Select
isMulti
className="formItem"
options={bots}
closeMenuOnBlur={false}
closeMenuOnSelect={false}
components={{Option: OptionWithSuffix}}
styles={selectStyles}
/>
}
rules={{ required: true }}
placeholder="Select bots"
name="bots"
rules={{ required: true }}
register={register}
setValue={setValue}
/>
}
{errors.platform && connectionType.value === "specific" && "Bots are required"}
<input className="formItem" type="submit" placeholder="Connect"/>
</form>
</div>
</Collapsible>
);
}
export default Connect;
Here's how I made my MUI-Select take multiple values using react-hook-forms in v6.14.1.
<Controller
name="categories"
type="select"
control={control}
defaultValue={categoryOptions[0]}
render={({ onChange, onBlur, value }) => (
<RHFSelect
onChange={onChange}
onBlur={onBlur}
value={[...value]} // this is what you need to do
multiple={true}
options={categoryOptions}
/>
)}
/>
RHFSelect is a reusable Select in my code. To select only a single value, you may do to like this -
value={value}
For v7.9.0, it can be done like this
<Controller
name="colors"
type="select"
control={control}
defaultValue={[]}
render={({ field }) => (
<Select
{...field}
multiple
>
/* Select ops */
</Select>
)}
/>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With