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;