I want to retrieve the value from my selection so I can make post requests. I have no problem getting from text input, but for some reason I can't get it from the drop down menu select. I end up getting a
"TypeError: Cannot read property 'value' of undefined"
Here is the code I am using.
import React from "react";
import { Form, Input, Button, Select } from "antd";
const { Option } = Select;
class ItemForm extends React.Component {
handleFormSubmit = event => {
event.preventDefault();
const name = event.target.elements.name.value;
const description = event.target.elements.description.value;
const category = event.target.elements.category.value;
console.log(name, description, this.refs.category.value);
};
render() {
return (
<div>
<Form onSubmit={this.handleFormSubmit}>
<Form.Item label="Form Layout" />
<Form.Item label="Product Name">
<Input name="name" placeholder="Ex: Organic Apple..." />
</Form.Item>
<Form.Item label="Description">
<Input name="description" placeholder="Ex: Juicy organic apples!" />
</Form.Item>
<Form.Item label="Category">
<Select name="category" placeholder="Please select a category">
<Option value="Fruit">Fruit</Option>
<Option value="Vegetable">Vegetable</Option>
<Option value="Poultry">Poultry</Option>
</Select>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
</div>
);
}
}
export default ItemForm;
Use onChange which is fired when the value of the select changes. antd select documentation
<Form.Item label="Category">
<Select
onChange={(value) => {
alert(value)
}}
name="category"
placeholder="Please select a category">
<Option value="Fruit">Fruit</Option>
<Option value="Vegetable">Vegetable</Option>
<Option value="Poultry">Poultry</Option>
</Select>
</Form.Item>
working example
Something similar to the classic javascript approach, you intended to use, could be use getFieldValue.
But coupling to coherent createRef , Form and Form.Item as below.
When getting values, remember to reference the Form.Item name and not the Input one ;-)
I have created a sandbox demo hoping other people will enjoy or contribute.
import React from "react";
import { Form, Input, Button, Select } from "antd";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css"; //export default ItemForm;
const { Option } = Select;
class ItemForm extends React.Component {
formRef = React.createRef();
handleFormSubmit = event => {
event.preventDefault();
console.log("All field values", this.formRef.current.getFieldsValue());
const name = this.formRef.current.getFieldValue("productName"); //OLD event.target.elements.name.value;
const description = this.formRef.current.getFieldValue("description"); //OLD event.target.elements.description.value;
const category = this.formRef.current.getFieldValue("category"); //OLD event.target.elements.category.value;
console.log(name, description, category);
alert(`${name}, ${description}, ${category}`);
};
render() {
return (
<div>
<Form ref={this.formRef} onSubmit={this.handleFormSubmit}>
<Form.Item label="Form Layout (Form.Item-createRef-getFieldValue Example)" />
<Form.Item label="Product Name" name="productName">
<Input name="name" placeholder="Ex: Organic Apple..." />
</Form.Item>
<Form.Item label="Description" name="description">
<Input name="description" placeholder="Ex: Juicy organic apples!" />
</Form.Item>
<Form.Item label="Category" name="category">
<Select name="category" placeholder="Please select a category">
<Option value="Fruit">Fruit</Option>
<Option value="Vegetable">Vegetable</Option>
<Option value="Poultry">Poultry</Option>
</Select>
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
onClick={this.handleFormSubmit}
>
Submit
</Button>
</Form.Item>
</Form>
</div>
);
}
}
ReactDOM.render(<ItemForm />, document.getElementById("container"));
Managed it by using onChange as shown below this.
state = {
status: ""
};
<Form.Item label="Status">
<Select
name="status"
onChange={value => {
this.setState({ status: value });
}}
placeholder="Please choose the status"
>
<Option value="new">New</Option>
<Option value="open">Open</Option>
<Option value="rejected">Rejected</Option>
<Option value="deferred">Deferred</Option>
<Option value="reopened">Reopened</Option>
</Select>
</Form.Item>
unlike what you would expect, in Ant Design Select you can't get the value by calling:
onChange={(e)=>console.log(e.target.value)}
no. instead you get the value directly by just accessing the event itself, like this:
onChange={(e)=>console.log(e)}
You can fix this as follows. (Works fine for Antd)
<Select
onChange={(text, index) => {
console.log(index.children);
}}
>
A better and cleaner way to avoid this issue is to define all the form values and labels in an array and pass it into the . Declare or use an onChange event and store the value of the event in your state.
Related
I have integrated react-hook-form with my form, where it is not registering field with name has single quote in the middle. Instead it stripes single quote .
import React from "react";
import ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import "./styles.css";
//App component
export default function App() {
const { register, handleSubmit } = useForm();
//onSubmit Handler
const onSubmit = (data) => console.log(data);
//field name value
const name = "Patient's Name";
//Return
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register(`register ${name}`)} />
<select {...register("gender")}>
<option value="female">female</option>
<option value="male">male</option>
<option value="other">other</option>
</select>
<input type="submit" />
</form>
);
}
//Renders
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
output : {register Patients Name: undefined, gender: "female"}
It looks like you are trying to assign the input field name as the value. You could try using the 'name' as the field name and ${name} as the input value.
Something like this may work better:
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("name")} />
<select {...register("gender")}>
<option value="female">female</option>
<option value="male">male</option>
<option value="other">other</option>
</select>
<input type="submit" />
</form>
If you really need a dynamic input field name, you could try:
const name = "Patient\'s Name";
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register({name})} />
<select {...register("gender")}>
<option value="female">female</option>
<option value="male">male</option>
<option value="other">other</option>
</select>
<input type="submit" />
</form>
);
}
I'm using react-hook-form to handle the update for some input fields.
Sometimes the users don't change on all fields, so I do not want to send all changed and unchanged fields with the update API. so my question is there a way to do that ? and just send the changed fields ?
You have formState.dirtyFields property that will return all the user modified inputs.
import * as React from "react";
import { useForm, useFormState } from "react-hook-form";
export default function App() {
const { register, handleSubmit, formState } = useForm();
const onSubmit = (data) => {
console.log(formState.dirtyFields);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("firstName")} placeholder="First name" />
<input {...register("lastName")} placeholder="Last name" />
<select {...register("category")}>
<option value="">Select...</option>
<option value="A">Category A</option>
<option value="B">Category B</option>
</select>
<input type="submit" />
</form>
);
}
The below code is from my NewFighterForm.js file within my app's component folder:
import React from 'react';
import { updateNewFighterForm } from '../actions/newFighterForm.js';
import { connect } from 'react-redux';
import Button from 'react-bootstrap/esm/Button';
const NewFighterForm = ({ formData, updateNewFighterForm, handleSubmit, editMode }) => {
const {name, alias, nationality, division, stance, height, reach, status, champion, win, loss, draw, ko} = formData
const handleChange = event => {
const { name, value } = event.target
debugger
updateNewFighterForm(name, value)
}
return (
<div>
<h1>Add Fighter</h1>
<form onSubmit={event => {
event.preventDefault()
debugger
handleSubmit(formData)
}}>
<ol>
<ul>
<label>Add Name: </label>
<br></br>
<input
placeholder="Enter Name"
name="name"
onChange={handleChange}
value={name}
/>
</ul>
<label>Add Alias: </label>
<br></br>
<ul>
<input
placeholder="Enter Alias"
name="alias"
onChange={handleChange}
value={alias}
/>
</ul>
<label>Add Nationality: </label>
<br></br>
<ul>
<input
placeholder="Enter Nationality"
name="nationality"
onChange={handleChange}
value={nationality}
/>
</ul>
<label>Choose Division: </label>
<br></br>
<select name="division"
value={"" + division}
onChange={handleChange}>
<option disabled>Choose the following weight division</option>
<option value="Flyweight">Flyweight</option>
<option value="Bantamweight">Bantamweight</option>
<option value="Featherweight">Featherweight</option>
<option value="Lightweight">Lightweight</option>
<option value="Welterweight">Welterweight</option>
<option value="Middleweight">Middleweight</option>
<option value="Light Heavyweight">Light Heavyweight</option>
<option value="Cruiserweight">Cruiserweight</option>
<option value="Heavyweight">Heavyweight</option>
</select>
<br></br>
<label>Choose Fighter's Stance: </label>
<br></br>
<select name="stance"
value={"" + stance}
onChange={handleChange}>
<option disabled>Choose the following stance type</option>
<option value="Orthodox">Orthodox</option>
<option value="Southpaw">Southpaw</option>
</select>
<br></br>
<label>Add Height: </label>
<br></br>
<input
placeholder="Enter Height (i.e 5 ft 5 in)"
name="height"
onChange={handleChange}
value={height}
/>
<br></br>
<label>Add Reach: </label>
<br></br>
<input
placeholder="Enter Reach (i.e 68 in)"
name="reach"
onChange={handleChange}
value={reach}
/>
<br></br>
<label>Are they still fighting?</label>
<br></br>
<select name="status"
value={"" + status}
onChange={handleChange}>
<option disabled>Choose whether fighter is still competing</option>
<option value="inactive">inactive</option>
<option value="active">active</option>
</select>
<br></br>
<label>Check if they ever were a World Champion</label>
<input
type="checkbox"
name="champion"
defaultChecked={false}
value={champion}
/>
<br></br>
<label>W:</label>
<input
placeholder="Enter number of wins"
name="win"
type="number"
pattern="[0-200]*"
inputMode="numeric"
onChange={handleChange}
value={win}
/>
<br></br>
<label>L:</label>
<input
placeholder="Enter number of losses"
name="loss"
type="number"
pattern="[0-200]*"
inputMode="numeric"
onChange={handleChange}
value={loss}
/>
<br></br>
<label>D:</label>
<input
placeholder="Enter number of draws"
name="draw"
type="number"
pattern="[0-200]*"
inputMode="numeric"
onChange={handleChange}
value={draw}
/>
<br></br>
<label>KO:</label>
<input
placeholder="Enter number of KO"
name="ko"
type="number"
pattern="[0-200]*"
inputMode="numeric"
onChange={handleChange}
value={ko}
/>
<br></br>
<label>List for Fighter: </label>
<br></br>
<select name="lists"
onChange={handleChange}>
<option disabled>Choose List for Fighter</option>
</select>
<br></br>
<Button
type="submit"
value={ editMode ? "Update Fighter" : "Create Fighter" }
>Create Fighter</Button>
</ol>
</form>
</div>
)
}
const mapStateToProps = state => {
// debugger
return {
formData: state.newFighterForm,
lists: state.myLists
}
};
export default connect(mapStateToProps, { updateNewFighterForm })(NewFighterForm)
The bottom of my form is where I would like to input the drop down select for the user to choose which one of their lists they would like to add the fighter to. In my debugger placed within mapStateToProps, I see the array of lists that is defined in "lists: state.myLists". I would like to be able to properly map the array and show the select option for the user to choose the list. I assume that I will need to add "lists" as one of the destructed properties defined for NewFighterForm (let me know if my understanding is correct). Please let me know if any more details are needed and I will update the post accordingly. Thank You all, much appreciated!
Yes, destructure lists from the props object and, assuming lists is an array, map as normal JSX. Here I'm assuming the lists array contains element objects that have a value and label property to display from, this will need to be tweaked to fit your actual lists array element shape.
const NewFighterForm = ({
formData,
updateNewFighterForm,
handleSubmit,
editMode,
lists, // <-- get lists from props
}) => {
...
return (
<div>
<h1>Add Fighter</h1>
<form
onSubmit={event => {
event.preventDefault()
debugger
handleSubmit(formData)
}}
>
<ol>
...
<label>List for Fighter: </label>
<br></br>
<select
name="lists"
onChange={handleChange}
>
<option disabled>Choose List for Fighter</option>
{lists.map(listItem => ( // <-- map the list
<option value={listItem.value}> // <-- the option value
{listItem.label} // <-- what you want displayed
</option>
))}
</select>
...
</ol>
</form>
</div>
)
};
const mapStateToProps = state => {
// debugger
return {
formData: state.newFighterForm,
lists: state.myLists
}
};
I am using AntD select.
When I put select into a separate component, the form does not see the value
Tell me why the form does not receive data on submit ?
Example
const SelectCust = () => {
return (
<Select
mode="multiple"
placeholder="Please select favourite colors"
style={{ width: 500 }}
name="select-multiple"
>
<Option value="red">Red</Option>
<Option value="green">Green</Option>
<Option value="blue">Blue</Option>
</Select>
)};
const Demo = () => {
const onFinish = values => {
console.log("Received values of form: ", values); // {custom:undefined}
};
return (
<Form name="validate_other" onFinish={onFinish}>
<Form.Item name="custom" label="Select custom">
<SelectCust />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
)};
If you wrap <Select> with <Form.Item> it works correctly:
const SelectCust = () => {
return (
<Form.Item name="custom" label="Select custom">
<Select
mode="multiple"
placeholder="Please select favourite colors"
style={{ width: 500 }}
name="select-multiple"
>
<Option value="red">Red</Option>
<Option value="green">Green</Option>
<Option value="blue">Blue</Option>
</Select>
</Form.Item>
);
};
Here is updated Demo component:
const Demo = () => {
const onFinish = values => {
console.log("Received values of form: ", values);
};
return (
<Form name="validate_other" onFinish={onFinish}>
<SelectCust />
<Form.Item
wrapperCol={{
span: 12,
offset: 6
}}
>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
EDIT 1
It seemed wrong to me that wrapping <Select> with <Form.Item> solved the problem, so I looked up <Form> documentation.
Here is updated code:
const SelectCust = props => {
return (
<Select
mode="multiple"
placeholder="Please select favourite colors"
onChange={props.onColorChange}
>
<Option value="red">Red</Option>
<Option value="green">Green</Option>
<Option value="blue">Blue</Option>
</Select>
);
};
const Demo = () => {
const [form] = Form.useForm();
const onFinish = values => console.log(values);
const handleColorChanged = value => {
form.setFieldsValue({ custom: value });
};
return (
<Form name="validate_other" form={form} onFinish={onFinish}>
<Form.Item name="custom" label="Select custom">
<SelectCust onColorChange={handleColorChanged} />
</Form.Item>
<Form.Item
wrapperCol={{
span: 12,
offset: 6
}}
>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
Update has 3 important steps:
Add const [form] = Form.useForm() in the beginning of Demo component
Implement handleColorChanged function and pass it as props to SelectCust. Handler sets form value by calling form.setFieldsValue().
Pass from as props to <Form> component
I am working with reach hooks and to validate my form fields I am using react-hook-form as it is the best option for now
SO to validating my normal input fields I am doing just ref={register({ required: true })} then on submit it is checking for errors as I am importing errors from react-hook-form
But when I am doing same for select field it is not checking the error object
This is what I am doing
<label htmlFor="func" className="form_label">
Select function
</label>
<select name="func" ref={register({ required: true })}>
<option selected disabled>
Select function
</option>
<option value="5">Function 2</option>
<option value="6">Function 3</option>
</select>
{errors.func && (
<div>
<span>Function is required</span>
</div>
)}
I don't know what I am missing
My actual code is with dynamic data
so I am looping it like this
<Form.Control as="select" custom>
<option disabled selected>Select role</option>
{loading === false &&
data.get_roles.map((li) => (
<option value={li.user_type_id}>
{li.user_type}</option>
))}
</Form.Control>
React hook form
try this code. I tried and it worked fine:
<label htmlFor="func" className="form_label">
Select function
</label>
<select name="func"
ref={register({
required: "select one option"
})}>
<option value=""></option>
<option value="5">Function 2</option>
<option value="6">Function 3</option>
</select>
{errors.func && <p style={{color:'red'}}> {errors.func.message}</p> }
//This Works for me
import React from 'react'
import { useForm } from "react-hook-form";
function Test() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = data => console.log(data);
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<select
className="custom-select"
id="selectmethod"
defaultValue=""
name="exampleRequired"
{...register("exampleRequired", { required: true })}
>
<option value="" disabled>Select Option</option>
<option value="1">Blue</option>
<option value="2">Red</option>
</select>
{errors.exampleRequired && <span className="formError errorMssg">This field is required</span>}
<br/>
<button type="submit" >SUBMIT </button>
</form>
</div>
)
}
export default Test
I think that I good way to validate forms is with yup. It gives you a lot of tools for validation and its integration with RHF is seamlessly: How to integrate yup with react-hook-form
import React from "react";
import ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "#hookform/resolvers/yup";
const schema = yup.object().shape({
mySelect: yup.string().notOneOf([""], "You must select an option!")
});
function MyForm() {
const {
register,
handleSubmit,
formState: { errors }
} = useForm({
defaultValues: {
mySelect: ""
},
resolver: yupResolver(schema)
});
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<select {...register("mySelect")}>
<option value="" disabled>
Select option
</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
<button type="submit">Submit </button>
{errors.mySelect && <p className="error">{errors.mySelect.message}</p>}
</form>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<MyForm />, rootElement);
Sandbox: Simple select validation with yup