I am attempting to parse the dropdown item that is selected into a Prop. I get an error of
TypeError: Cannot read property 'name' of undefined
I have attempted to make a new Prop and parse it through there and that did not work either. I have also tried to add all array into the formData to no avail.
When I do not add the dropdown menu the Format and Location are parsed no problem, as soon as they change to the dropdown's it does not get read. I think the dropdowns change them into another format and not a string. .
The addFixture.js code is below:
const AddFixture = ({ addFixture, history }) => {
const [formData, setFormData] = useState({
teams: '',
opposition: '',
date: '',
format: '',
location: '',
});
const {
teams,
opposition,
date,
format,
location
} = formData;
const overs = [
{ value: '40 Overs', label: '40 Overs' },
{ value: '20 Overs', label: '20 Overs' },
]
const hOrA = [
{ value: 'Home', label: 'Home' },
{ value: 'Away', label: 'Away' },
]
const onChange = (e) =>
setFormData({ ...formData, [e.target.name]: e.target.value });
<form
className="form"
onSubmit={(e) => {
e.preventDefault();
addFixture(formData, history);
}}
>
<div className="form-group">
<Select
type="text"
placeholder="* Location"
name="location"
value={location}
options={hOrA}
onChange={onChange}
required
/>
</div>
<div className="form-group">
<Select
type="text"
placeholder="* format"
name="format"
value={format}
onChange={onChange}
options={overs}
required
/>
</div>
</form>
);
};
AddFixture.propTypes = {
addFixture: PropTypes.func.isRequired
};
React select expects the value to match one of the dropdown values - So
value={location} //needs to match the object {value:string, label:string}
React select passes in the full value of the selected option to the onChange so
onChange={onChange} //onChange={(val:{value:label}) => {}}
You can pass in the name of the dropdown like so
onChange={(val) => onChange(val,nameOfDropdown )}
Then use the name passed in like so
const onChange = (selected, name) =>{
setFormData({ ...formData, [name]: selected.value });
}
With all these changes your code should look something like this
const AddFixture = () => {
const [formData, setFormData] = useState({
format: '',
location: '',
});
const {
format,
location
} = formData;
const overs = [
{ value: '40 Overs', label: '40 Overs' },
{ value: '20 Overs', label: '20 Overs' },
]
const hOrA = [
{ value: 'Home', label: 'Home' },
{ value: 'Away', label: 'Away' },
]
/** onChange will get passed value from the dropdown {value:string, label:string} */
const onChange = (selected, name) =>{
setFormData({ ...formData, [name]: selected.value });
}
return(
<>
<div className="form-group">
<Select
type="text"
placeholder="* Location"
name="location"
/** Need to set this to one of the dropdown values {value:string, label:string} */
value={hOrA.find((val) => val.value === location)}
options={hOrA}
onChange={(val) => onChange(val,"location" )}
required
/>
</div>
<div className="form-group">
<Select
type="text"
placeholder="* format"
name="format"
/** Need to set this to one of the dropdown values {value:string, label:string} */
value={hOrA.find((val) => val.value === format)}
onChange={(val) => onChange(val,"format" )}
options={overs}
required
/>
</div>
</>
);
};
Related
The state variables are -
const [details, setDetails] = useState({
Name: "",
Number: null,
subject= [{
subject1 : "",
subject2 : ""
}]
})
<input type="text" placeholder="Enter name" value={details.Name} onChange={(e) => setDetails({ ...details, Name: e.target.value })} />
<input type="text" placeholder="Enter number" value={details.Number} onChange={(e) => setDetails({ ...details, Number: e.target.value })} />
I dont know how to write access the subjects respectively
try this the minor change "subject =" to "subject :"
const [count, setCount] = useState(0);
const [details, setDetails] = useState({
Name: "saqib",
Number: null,
subject: [
{
subject1: "1",
subject2: "2",
},
],
});
Try This to access the object
{details.name}
{details.subject[0].subject1}
Here is a generic method that handles multiple elements inside the subjects array
{
details.subject.map((obj,index) => {
return Object.entries(obj).map(k => {
return <input type="text" value={k[1]} onChange={(e) => setDetails({ ...details, subject: Object.assign([ ...details.subject], { [index]: {...details.subject[index], [k[0]]: e.target.value }}) })} />
})
})
}
demo
Assuming you only want to change the first object of the array in this case, it would look like this:
<input
type="text"
placeholder="Enter subject1"
value={details.subject[0].subject1}
onChange={(e) =>
setDetails((curr) => ({
...curr,
subject: [
{ ...curr.subject[0], subject1: e.target.value },
...curr.subject,
],
}))
}
/>
<input
type="text"
placeholder="Enter subject2"
value={details.subject[0].subject2}
onChange={(e) =>
setDetails((curr) => ({
...curr,
subject: [
{ ...curr.subject[0], subject2: e.target.value },
...curr.subject,
],
}))
}
/>
Please note that the curr callback is to use the current state. This is better than using the details state, since it is set async.
As a Begineer in a react,here what i want to do is when i select options from the react-select option the i want to dispaly that selected options label in the text input down above on both field but got problem with here onchanges event How to change value in text field from the select options along with selected value?
Code
import React, { useState } from "react";
const initialValues = [{ number: "", options: "" }];
const countries = [
{ id: 1, name: USA },
{ id: 2, name: Russia },
{ id: 3, name: UK },
];
const Newrow = (props) => {
const [number, setNumber] = useState("");
const [options, setoption] = useState([]);
const [options1, setoption1] = useState("");
const addRow = () => {
let _row = {
number: "",
options: "",
};
props.setData(_row);
};
return (
<div>
<input
type="number"
value={number}
onChange={(e) => {
setNumber(e.target.value);
}}
/>
<input
type="text"
className="input"
value={options}
onChange={options1}
/>
<input
type="text"
className="input"
value={options}
onChange={options1}
/>
</div>
);
};
export default function App(props) {
const [data, setData] = useState([]);
const addRow = (row) => {
setData([...data, row]);
};
return (
<div className="App">
<Select
options={countries}
value={{
label: name,
value: id,
}}
onChange={(e) => {
const res = {
id: e.value,
name: e.label,
};
console.log(res);
setoption1(res);
}}
></Select>
{[...data, ...initialValues].map((row, idx) => {
return (
<Newrow
setData={addRow}
data={row}
key={idx}
delrow={idx}
options1={options1}
/>
);
})}
<button
type="submit"
onClick={() => addRow({ number: "", options: "" })}
className="btn btn-success"
>
Add
</button>
</div>
);
}
You appear to be passing in options1 (a string) into the onChange handler of your text inputs. The onChange handler should only take a function, that will be triggered when those inputs change.
You can discard the onChange={options1} property and change the value property to value={options1}.
Change this:
<input type="text"
className="input"
value={options}
onChange={options1}
/>
to this:
<input type="text"
className="input"
value={options1}
/>
I have a react select component I want to update data so when I fetch data from my API i want to set my data to react select I use this code.
I have create state and use useEffect to fetch data from an API
const [currency, setCurrency] = useState([]);
useEffect(() => {
if (sessionStorage.getItem('AccountId')) {
axios.get(process.env.REACT_APP_SQL_API_URL + 'Organisation/' + query.search.split('=')[1])
.then(res => {
setCurrency({ label: res.data[0].currency, value: res.data[0].currency });
})
.catch(error => console.log(error.response))
}
else {
history.push('/login')
}
}, [])
This is the react select code
<Controller
rules={{ required: true }}
name="currency"
control={control}
render={({ field: { onChange } }) =>
<Select
onChange={(e) => {
onChange(e)
//handleSelect(e)
}}
options={[{ value: 'USD', label: 'USD $' }, { value: 'TZS', label: 'TZS' }]}
defaultValue={[currency]}
/>
}
/>
you need to have a state :
const [currency, setCurrency] = useState([]);
<Controller
rules={{
required: true }}
name="currency"
control={control}
render={({ field: { onChange } }) =>
<Select
onChange={(e) => {
onChange(e)
//handleSelect(e)
console.log(e.target);
console.log(e.target.value);
setCurrency(state=>[ //change state ])
}}
options={[{ value: 'USD', label: 'USD $' }, { value: 'TZS', label: 'TZS' }]}
defaultValue={currency}
value={currency}
/>
}
/>
My React App has over 20 fields on form tag. and my working code like the following
function DriverForm() {
const [formData1, setFormData1] = useState(1);
const [formData2, setFormData2] = useState(2);
const [formData3, setFormData3] = useState(3);
const [formData4, setFormData4] = useState(4);
const [formData5, setFormData5] = useState(5);
const handleSubmit = (event) => {
event.preventDefault();
};
return (
<div className="wrapper">
<form onSubmit={handleSubmit}>
<input value={formData1} onChange={(e) => setFormData1(e.target.value)} />
<input value={formData2} onChange={(e) => setFormData2(e.target.value)} />
<input value={formData3} onChange={(e) => setFormData3(e.target.value)} />
<input value={formData4} onChange={(e) => setFormData4(e.target.value)} />
<input value={formData5} onChange={(e) => setFormData5(e.target.value)} />
<button type="submit">Submit</button>
</form>
</div>
);
}
as you see, my code has the code repeat to declare the input field state variable like const [formData5, setFormData5] = useState(5);
and At the rendering function, onChange={(e) => setFormData1(e.target.value)} was repeated lots of time.
is there any way to be simple and look professional by using json object and other?
I suppose you could make the code more DRY by combining the duplicated bits.
Single state object holding field values by input name
Single change handler to handle merging in state updates
Assign inputs a name attribute to be passed with change event
DRY solution
function DriverForm() {
const [formData, setFormData] = useState({
data1: 1,
data2: 2,
// etc...
});
const handleSubmit = (event) => {
event.preventDefault();
// handle formData
};
const handleChange = event => {
event.preventDefault();
const { name, value } = event.target;
setFormData(formData => ({
...formData,
[name]: value,
}))
}
return (
<div className="wrapper">
<form onSubmit={handleSubmit}>
<input value={formData.data1} name="data1" onChange={handleChange} />
<input value={formData.data2} name="data2" onChange={handleChange} />
// etc..
<button type="submit">Submit</button>
</form>
</div>
);
}
If you know ahead of time what the input types will be you can load them into a configuration array and map them
const fieldData = [
{
name: 'data1',
type: 'text',
},
// etc...
];
...
<form onSubmit={handleSubmit}>
{fieldData.map(({ name, type }) => (
<input
key={name{
type={type}
value={formData[name]}
name={name}
onChange={handleChange}
/>
))}
<button type="submit">Submit</button>
</form>
There are many answers to this question. However, one of the rules I like to code by is 'Everything is an array'.
I would start by creating an array of form fields. Then take those form fields, and build the initial state object to be used in the formData state hook.
const formFields = [
{ name: 'field1', type: 'text', initialValue: '1' },
{ name: 'field2', type: 'text', initialValue: '2' },
{ name: 'field3', type: 'text', initialValue: '3' },
{ name: 'field4', type: 'text', initialValue: '4' },
{ name: 'field5', type: 'text', initialValue: '5' },
];
const initalState = formFields.reduce(
(acc, next) => ({ ...acc, [next.name]: next.initalValue }),
{}
);
Then create a single handleChange handler.
const handleChange = (name, value) => {
setFormData({
...formData,
[name]: value,
});
};
And finally, map the formFields in your render function.
{formFields.map(({ name, type }) => (
<input
key={name}
name={name}
type={type}
value={formData[name]}
onChange={(e) => handleChange(e.target.value, name)}
/>
))}
Complete code:
const formFields = [
{ name: 'field1', type: 'text', initialValue: '1' },
{ name: 'field2', type: 'text', initialValue: '2' },
{ name: 'field3', type: 'text', initialValue: '3' },
{ name: 'field4', type: 'text', initialValue: '4' },
{ name: 'field5', type: 'text', initialValue: '5' },
];
const initalState = formFields.reduce(
(acc, next) => ({ ...acc, [next.name]: next.initalValue }),
{}
);
function DriverForm() {
const [formData, setFormData] = useState(initalState);
const handleChange = (name, value) => {
setFormData({
...formData,
[name]: value,
});
};
const handleSubmit = (event) => {
event.preventDefault();
};
return (
<div className="wrapper">
<form onSubmit={handleSubmit}>
{formFields.map(({ name, type }) => (
<input
key={name}
name={name}
type={type}
value={formData[name]}
onChange={(e) => handleChange(e.target.value, name)}
/>
))}
<button type="submit">Submit</button>
</form>
</div>
);
}
Working with forms in react is one of the most challenging problems to solve when developing applications. The best way to work with the form is react-hook-form. It is introduced so that the performance or latency of the react application will be improved to such a great extend. There is another way of creating a form in react is Formik.
Both Formik and React Hook Form are solving the same problem (provides a way to store form data in local state), but React Hook Form is a way of creating a form with uncontrolled components and the hooks allow it to give a better performance result than Formik.
I'll give you one simple example of react-hook-form.
import React, {useState} from "react";
import { useForm } from "react-hook-form";
function App() {
const { register, handleSubmit } = useForm();
const {data, setData} = useState({});
const onSubmit = (formData) => {
console.log(formData); // an object of form Data
setData(formData);
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="formInput1" ref={register} />
<input name="formInput2" ref={register} />
<input type="submit" />
</form>
);
}
When we console log the form data we get an object of form input.
>console
object{
formInput1: "your value",
formInput2: "your 2nd value"
}
You can store this object in a local state or in a database through API call.
I'll recommend you to learn both Formik and React hook form then you will be able to differentiate between them. Yes, react-hook-form is the best way to create a form in react in terms of performance. Formik lets you easy to understand and create a form in one go.
Others have given the best example of creating a react form without using formik and react hook form.
import React, { useState } from 'react'
const App=()=>{
const[fullname, setFullName]=useState({
fname:"",
lname:"",
email:"",
phone:""
});
const inputEvent=(event)=>{
// const value = event.target.value;
// const name = event.target.name;
const {name,value}=event.target;
setFullName((preValue)=>{
console.log(preValue)
console.log(value)
return{
...preValue,//gets the previous value
[name]:value//sets the value to the field that is getting changed
}
})
}
const submits=(event)=>{
event.preventDefault();
alert('Form has been submitted')
}
return(
<>
<div>
<form onSubmit={submits}>
<div>
<h1>Hello {fullname.fname} {fullname.lname}</h1>
<div className='container'>
<p>{fullname.email}</p>
</div>
{/* <p>{fullname.phone}</p> */}
<input type='text'
placeholder='Enter Your Name'
onChange={inputEvent}
name='fname'
value={fullname.fname}
/>
<input type='text'
placeholder='Enter Your Last Name'
onChange={inputEvent}
name='lname'
value={fullname.lname}
/>
<input type='text'
placeholder='Enter Your Email'
onChange={inputEvent}
name='email'
value={fullname.email}
/>
<input type='phone'
placeholder='Enter Your Phone Number'
onChange={inputEvent}
name='phone'
value={fullname.phone}
/>
<button type='submit'>Submit</button>
</div>
</form>
</div>
</>
)
};
export default App;
This how handle too much of inputs in a form you can extend it in your desired way
What you should do is just replace the names and values in your way like formData1
I've got a form component, where you can add multiple inputs on click. I am using useState to store data from all of the inputs, but the problem is I am receiving a message that my inputs are uncontrolled
State data structure
const [formData, setFormData] = useState({
title: '',
titleNum: '',
meta: [
{
content: '',
contentNum: '',
details: '',
tags: ''
}
]
});
The idea is to dynamically add multiple objects via form to meta array
onChange events added to inputs
const { title, titleNum, meta } = formData;
const handleMainChange = e => {
const { name, value } = e.target;
setFormData({ ...formData, [name]: value });
};
const handleSubChange = (e, index) => {
const { name, value } = e.target;
let metaContent = [...meta];
metaContent[index] = { ...metaContent[index], [name]: value };
setFormData({ ...formData, meta: metaContent });
};
Adding and removing inputs
const handleAddInput = () => {
setFormData(prevState => ({
meta: [
...prevState.meta,
{ content: '', contentNum: '', details: '', tags: '' }
]
}));
};
const handleRemove = index => {
let metaContent = [...meta];
metaContent.splice(index, 1);
setFormData({ meta: metaContent });
};
Submit and looping through all META
const onFormSubmit = e => {
e.preventDefault();
createAccount(formData, history);
};
const renderList = meta.map((item, index) => {
console.log(item);
return (
<AddMeta
key={index}
index={index}
meta={item}
handleChange={e => handleSubChange(e, index)}
handleRemove={() => handleRemove(index)}
/>
);
});
AddMeta Component
const AddMeta = ({ index, meta, handleChange, handleRemove }) => {
return (
<Fragment>
<div className='row valign-wrapper'>
<div className='col s3'>
<h5 className='indigo-text text-lighten-1'>Add Meta - {index + 1}</h5>
</div>
<div className='col s9'>
<button onClick={() => handleRemove(index)} className='btn red'>
Remove Meta Content
</button>
</div>
</div>
<div className='form-group'>
<input
type='text'
placeholder='Content Text'
name='content'
value={meta.content || ''}
onChange={e => handleChange(e, index)}
autoComplete='off'
/>
</div>
<div className='form-group'>
<input
type='text'
placeholder='Content Number'
name='contentNum'
value={meta.contentNum || ''}
onChange={e => handleChange(e, index)}
autoComplete='off'
/>
</div>
<div className='form-group'>
<textarea
className='materialize-textarea'
type='text'
placeholder='Details'
name='details'
value={meta.details || ''}
onChange={e => handleChange(e, index)}
autoComplete='off'
/>
</div>
<div className='form-group'>
<input
type='text'
placeholder='Tags'
name='tags'
value={meta.tags || ''}
onChange={e => handleChange(e, index)}
autoComplete='off'
/>
</div>
</Fragment>
);
};
I appreciate any attempt to solve this problem.
Thanks!
Try removing the || '' on your inputs value. Their value should always be tied to your state (which should be '' when you add a new meta).