I have a form in which I am generating form fields dynamically . Now Issue is fields which are not dynamically generated are getting input and those field which are dynamically generated are not taking input in fields. name='name' and name='age' fields are not getting input I cant insert data in them . while first two fields owner and description are working fine
import React from "react";
class Form extends React.Component {
state = {
cats: [{ name: "", age: "" }],
owner: "",
description: ""
};
handleChange = e => {
alert('inot');
if (["name", "age"].includes(e.target.className)) {
let cats = [...this.state.cats];
cats[e.target.dataset.id][
e.target.className
] = e.target.value.toUpperCase();
this.setState({ cats }, () => console.log(this.state.cats));
} else {
this.setState({ [e.target.name]: e.target.value.toUpperCase() });
}
};
addCat = e => {
this.setState(prevState => ({
cats: [...prevState.cats, { name: "", age: "" }]
}));
};
handleSubmit = e => {
e.preventDefault();
};
handleClick = e => document.getElementById(e.target.id).remove();
// changeOption =(e) => {
// this.setState.cats(e.target.value);
// }
render() {
let { owner, description, cats } = this.state;
return (
<form onSubmit={this.handleSubmit} onChange={this.handleChange}>
<div className='row mt-5'>
<div className='col-md-12 mb-5'>
<h3 className='text-center text-primary'>Create Products Option</h3>
</div>
<div className='col-md-3'></div>
<div className='col-md-3'>
<label htmlFor='name'>
<b>Owner</b>
</label>
<input
type='text'
className='form-control'
name='owner'
id='owner'
value={owner}
/>
</div>
<div className='col-md-3'>
<label htmlFor='description'>
<b>Description</b>
</label>
<input
type='text'
className='form-control'
name='description'
id='description'
value={description}
/>
<button
className='btn btn-success rounded-0 w-25 float-right mt-2 shadow-none'
onClick={this.addCat}
>
+
</button>
</div>
<div className='col-md-3'></div>
</div>
{cats.map((val, idx) => {
let catId = `cat-${idx}`,
ageId = `age-${idx}`;
return (
<form onSubmit={this.handleSubmit} onChange={this.handleChange}>
<div id={ageId} key={idx}>
<div className='row mt-5'>
<div className='col-md-3'></div>
<div className='col-md-3'>
<label htmlFor={catId}>
<b>{`Cat #${idx + 1}`}</b>
</label>
<input
type='text'
name='name'
data-id={idx}
id={catId}
value={cats[idx].name}
className='name form-control'
/>
</div>
<div className='col-md-3'>
<label htmlFor={ageId}>
<b>Age</b>
</label>
<input
type='text'
name='age'
data-id={idx}
id={ageId}
value={cats[idx].age}
className='age form-control'
/>
<button
className='btn btn-success rounded-0 w-25 float-right mt-2 shadow-none'
onClick={this.addCat}
>
+
</button>
<input
type='button'
name={ageId}
data-id={idx}
id={ageId}
value={"-"}
className='age float-right mt-2 mr-3 btn btn-danger w-25 rounded-0 w-25 shadow-none'
onClick={this.handleClick}
/>
</div>
<div className='col-md-3'></div>
</div>
</div>
</form>
);
})}
<div className='row mt-3'>
<div className='col-md-3'></div>
<div className='col-md-3'>
<label htmlFor='name'>
<b>Min Selection</b>
</label>
<input
type='text'
className='form-control'
name='min'
id='min'
value={""}
/>
</div>
<div className='col-md-3'>
<label htmlFor='description'>
<b>Max Selection</b>
</label>
<input
type='text'
className='form-control'
name='max'
id='max'
value={""}
/>
<input
className='float-right btn btn-success mt-3'
type='submit'
value='Submit'
/>
{/* <button className="float-right" onClick={this.addCat}>Add new cat</button> */}
</div>
<div className='col-md-3'></div>
</div>
</form>
);
}
}
export default Form;
Codesandbox here. Looks like you should be seeing some errors in your console that might provide some explanation:
Warning: Failed prop type: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.
Detailed answer
Working Sandbox here. There are a few things wrong here:
Your handleChange function needs to change
Your function is looking at className which won't work. Your inputs return form-control name for example. Instead try using the element's name.
Updated function:
handleChange = e => {
if (["name", "age"].includes(e.target.name)) {
let cats = [...this.state.cats];
cats[e.target.dataset.id][e.target.name] = e.target.value.toUpperCase();
this.setState({ cats }, () => console.log(this.state.cats));
} else {
this.setState({ [e.target.name]: e.target.value.toUpperCase() });
}
};
Aside
Do not render <form> elements in other <form> elements.
Don't do this. You don't need another form here. Just remove that.
// Line 76-80:
{cats.map((val, idx) => {
let catId = `cat-${idx}`,
ageId = `age-${idx}`;
return (
<form onSubmit={this.handleSubmit} onChange={this.handleChange}>
// ...
Related
I made a dynamic form ,but When I save the values that I got with API in haveFaq and map them ,removeFields and addFields does not work
I save the values that I receive from the API in haveFaq like this:
[
{ question: "question1", answer: "answer1" },
{ question: "question2", answer: "answer2" },
{ question: "question3", answer: "answer3" },
];
const [haveFaq, setHaveFaq] = useState([]);
const [tempData, setTempData] = useState([
{
question: "",
answer: "",
},
]);
const handleFormChange2 = (event, index) => {
let data = [...tempData];
data[index][event.target.name] = event.target.value;
setTempData(data);
};
const addFields = () => {
let object = {
question: "",
answer: "",
};
setTempData([...tempData, object]);
};
const removeFields = (index) => {
let data = [...tempData];
data.splice(index, 1);
setTempData(data);
};
Attention
But if we map tempData instead of haveFaq The form works correctly,Because its initial values are empty
<>
{haveFaq.map((form, index) => {
return (
<div key={index + 1}>
<div className="form-row">
<div className="col-md-11 mb-3">
<label className="font-weight-bold" for="">
question
</label>
<input
className="form-control"
name="question"
placeholder="question"
onChange={(event) => handleFormChange2(event, index)}
value={form.question}
/>
</div>
<div className="col-md-1 mt-4 iconSelf">
<p
className="btnRemove"
type="button"
onClick={() => removeFields(index)}
>
❌
</p>
</div>
</div>
<div className="form-row">
<div className="col-md-12 mb-3">
<label className="font-weight-bold" for="">
answer
</label>
<textarea
rows={5}
className="lineHeightAnswer form-control"
name="answer"
placeholder="answer"
onChange={(event) => handleFormChange2(event, index)}
value={form.answer}
/>
</div>
</div>
<hr />
</div>
);
})}
<div>
<button
className="btn btn-info"
type="button"
onClick={addFields}
>
Add New
</button>
</div>
</>
I keep running into this error. I have tried defining the variables but it still does not seem to work. I may be doing this wrong or its something very small but i cant seem to figure it out. I would appreciate the help.
Uncaught ReferenceError: process is not defined
this is another error which im guessing is from the same issue.
This is my code below:
import React, { Component } from "react";
import { Button, Form, FormGroup, Label, Input, Col, Row } from "reactstrap";
import Loading from "./LoadingComponent";
class Issue extends Component {
constructor(props) {
super(props);
this.state = {
isbn: "",
roll: "",
};
}
componentDidMount() {
window.scrollTo(0, 0);
}
render() {
if (this.props.booksLoading || this.props.usersLoading) {
return (
<div className="container">
<div className="row">
<Loading />
</div>
</div>
);
} else if (this.props.booksErrMess) {
return (
<div className="container loading">
<div className="row heading">
<div className="col-12">
<br />
<br />
<br />
<br />
<h3>{this.props.booksErrMess}</h3>
</div>
</div>
</div>
);
} else if (this.props.usersErrMess) {
return (
<div className="container loading">
<div className="row heading">
<div className="col-12">
<br />
<br />
<br />
<br />
<h3>{this.props.usersErrMess}</h3>
</div>
</div>
</div>
);
}
else {
const bookoptions = this.props.books.map((book, index) => (
<option key={book.isbn}>{book.isbn}</option>
));
const defaultBook = this.props.books[0];
// To just get list of the students (not the admins)
let useroptions = this.props.users.filter((user) => !user.admin);
const defaultUser = useroptions[0];
useroptions = useroptions.map((user, index) => (
<option key={user.roll}>{user.roll}</option>
));
if (this.state.isbn === "") {
this.setState({ isbn: defaultBook.isbn, roll: defaultUser.roll });
}
return (
<div className="container full">
<div className="row justify-content-center heading">
<div className="col-12">
<h3 align="center"> Issue book</h3>
</div>
</div>
<div className="row row-content justify-content-center">
<Form
onSubmit={(e) => {
let bookid = this.props.books.filter(
(book) => book.isbn === this.state.isbn
)[0]._id;
let studentid = this.props.users.filter(
(user) => user.roll === this.state.roll
)[0]._id;
this.props.postIssue(bookid, studentid);
e.preventDefault();
}}
>
<FormGroup row>
<Label htmlFor="isbn"> ISBN No.of book</Label>
<Input
type="select"
defaultValue={defaultBook.name}
name="isbn"
id="isbn"
className="form-control"
onInput={(e) => {
this.setState({ isbn: e.target.value });
}}
>
{bookoptions}
</Input>
</FormGroup>
<FormGroup row>
<Label htmlFor="roll"> Roll No. of student </Label>
<Input
type="select"
id="roll"
className="form-control"
onInput={(e) => {
this.setState({ roll: e.target.value });
}}
>
{useroptions}
</Input>
</FormGroup>
<FormGroup row>
<Label htmlFor="name"> Name of book </Label>
<Input
type="text"
id="name"
name="name"
placeholder="Name of Book"
defaultValue={defaultBook.name}
value={
!this.state.isbn
? ""
: this.props.books.filter(
(book) => book.isbn === this.state.isbn
)[0].name
}
className="form-control"
disabled
/>
</FormGroup>
<FormGroup row>
<Label htmlFor="author"> Authors </Label>
<Input
type="text"
id="author"
name="author"
placeholder="Name of authors"
defaultValue={defaultBook.author}
value={
!this.state.isbn
? ""
: this.props.books.filter(
(book) => book.isbn === this.state.isbn
)[0].author
}
className="form-control"
disabled
/>
</FormGroup>
<FormGroup row>
<Label htmlFor="name_student"> Name of student </Label>
<Input
type="text"
id="name_student"
name="name_student"
placeholder="Name of student"
defaultValue={
defaultUser.firstname + " " + defaultUser.lastname
}
value={
!this.state.roll
? ""
: this.props.users.filter(
(user) => user.roll === this.state.roll
)[0].firstname +
" " +
this.props.users.filter(
(user) => user.roll === this.state.roll
)[0].lastname
}
className="form-control"
disabled
/>
</FormGroup>
<FormGroup row>
<Label htmlFor="username"> Username of student </Label>
<Input
type="text"
id="username"
name="username"
placeholder="Username of student"
defaultValue={defaultUser.username}
value={
!this.state.roll
? ""
: this.props.users.filter(
(user) => user.roll === this.state.roll
)[0].username
}
className="form-control"
disabled
/>
</FormGroup>
<Row className="align-self-center">
<Col className="text-center">
<Button type="submit" className="bg-primary">
Submit
</Button>
</Col>
</Row>
</Form>
</div>
<br />
</div>
);
}
}
}
export default Issue;
Since the error is reading from undefined, the only possible source of error should be user.roll or defaultUser.roll.
let useroptions = this.props.users.filter((user) => !user.admin);
const defaultUser = useroptions[0];
useroptions = useroptions.map((user, index) => (
<option key={user.roll}>{user.roll}</option>
));
Add an undefined check before using defaultUser and useroptions.
if (defaultUser) {
...
}
I am new to reatJS. Can some genius please help me with my issue?
I am developing a e-pharmacy website where I let the users to first select a preferred districts and select a pharmacy from that area. I list out all the districts along with a drop down menu which shows all the pharmacies in that district. Then the user can make orders to the selected pharmacy.
I pass the selected pharmacy Id through the URL when user post an order.
If I had 3 districts, and I have selected a pharmacy from the third district, the URL appears like this:
http://localhost:3000/uploadlist?phmcy=&phmcy=&phmcy=1 (here the id of the selected pharmacy is "1")
The URL shows all the unselected values also. My preference is to display only the selected pharmacy's Id.
I want only the selected value to be displayed. Like this:
http://localhost:3000/uploadlist?phmcy=1
Here is my codes:
The district component: (Where the customer can select a district)
import React,{ Component } from 'react';
//import { Router } from 'react-router-dom/cjs/react-router-dom.min';
import { Link, withRouter } from "react-router-dom";
import './district.css';
import Upload from "./upload.component";
import Districtpharmacy from "./districtpharmacy.component";
class District extends React.Component{
state = {
pharmacy: []
};
render(){
return(
<div className="container">
<div className="container district">
<form action="/uploadlist" method="get">
<div className="row">
<div className="container header">
<h1>Select your District and Nearby Pharmacy</h1>
<p>Please select only one pharmacy</p>
</div>
</div>
<div className="row">
<div className="form-column">
<Districtpharmacy district="Mathara"/>
<Districtpharmacy district="Galle"/>
<Districtpharmacy district="Hambantota"/>
<Districtpharmacy district="Kalutara"/>
<Districtpharmacy district="Colombo"/>
<Districtpharmacy district="Gampaha"/>
<Districtpharmacy district="Rathnapura"/>
<Districtpharmacy district="Kurunegala"/>
</div>
<div className="form-column">
<Districtpharmacy district="Monaragala"/>
<Districtpharmacy district="Anuradhapura"/>
<Districtpharmacy district="Polonnaruwa"/>
<Districtpharmacy district="Kandy"/>
<Districtpharmacy district="Nuwara Eliya"/>
<Districtpharmacy district="Kegalla"/>
<Districtpharmacy district="Matale"/>
<Districtpharmacy district="Badulla"/>
<Districtpharmacy district="Ampara"/>
</div>
<div className="form-column">
<Districtpharmacy district="Puttalam"/>
<Districtpharmacy district="Trincomalee"/>
<Districtpharmacy district="Batticaloa"/>
<Districtpharmacy district="Mannar"/>
<Districtpharmacy district="Vavuniya"/>
<Districtpharmacy district="Mulaitivu"/>
<Districtpharmacy district="Kilinochchi"/>
<Districtpharmacy district="Jaffna"/>
</div>
</div>
<br/>
<button type="submit" >Add</button>
</form>
</div>
</div>
);
}
}
export default withRouter(District);
district.pharmacy component: (where the dropdown menu is returned.)
import React,{ Component } from 'react';
//import { Router } from 'react-router-dom/cjs/react-router-dom.min';
import { Link } from "react-router-dom";
import './district.css';
//import Upload from "./upload.component";
class Districtpharmacy extends React.Component{
state = {
pharmacy: [],
District: this.props.district
};
componentDidMount() {
console.log(this.state)
fetch("https://localhost:44357/api/Pharmacies?field=district&value="+this.props.district)
.then(response => response.json())
.then(pharmacy => this.setState({pharmacy: pharmacy}));
//console.log(this.state)
}
render(){
//console.log(this.state)
const pharm=this.state.pharmacy.map((pharmacies) =>{
return(<option value ={pharmacies.id}>{pharmacies.pharmacyname},{pharmacies.address}</option>)})
return(
<div>
<label class="label" for="district"><span>{this.props.district}</span></label>
<select name="phmcy" className="form-control select-dropdown"><option value="">Select Pharmacy</option>{pharm}</select>
</div>
)
}
}
export default Districtpharmacy
Can someone help me with this?
Edited:
The order posting form:
The parent file: (uploadlist.js)(the "phmcy" value is to be fetched from the url. this file is triggered after selecting the pharmacy)
export default function Uploadlist() {
let myphmcy = (new URLSearchParams(window.location.search)).get("phmcy")
console.log(myphmcy);
const ordersAPI= (url='https://localhost:44357/api/Orders') => {
return {
fetchAll: () => axios.get(url),
create: newRecord => axios.post(url, newRecord),
update: (id, updateRecord) => axios.put(url + id, updateRecord),
delete: id => axios.delete(url+id)
}
}
const addOrEdit = (formData, onSuccess) => {
ordersAPI().create(formData)
.then(res => {
onSuccess();
})
.catch(err => console.log(err.response.data))
}
return (
<div className="row">
<div className="jumbotron jumbotron-fluid py-4 "></div>
<div className="container text">
<h1 className="display-4"> Order Register</h1>
</div>
<div className="col-md-4 offset-3">
<Upload //this is the child component where the order form is built.
addOrEdit = {addOrEdit}
myphmcy = {myphmcy}
/>
</div>
<div className="col-md-1">
<div> </div>
</div>
</div>
)
}
the child file:
const initialFieldValues ={
orderID:0,
date_time:'',
status:'',
status2:'',
pharmacyName:'',
customerName:'',
patientName:'',
patientAge:'',
address:'',
email:'',
teleNo:'',
customer_id:1,
pharmacy_id:0,//data obtaind from the URL have to posted as the pharmacyID when posting.
image:'',
imageSource:'',
imageData: null
}
export default function Upload(props) {
const {addOrEdit} = props
const {myphmcy} = props
console.log(myphmcy);
const [values, setValues] = useState(initialFieldValues)
const[errors, setErrors] = useState({})
const handleInputChange= e => {
const {name, value} = e.target;
setValues({
...values,
[name]:value
})
}
/*const addOrEdit = (formData, onSuccess) => {
ordersAPI().create(formData)
.then(res => {
onSuccess();
})
.catch(err => console.log(err.response.data))
}*/
const showPreview = e => {
if(e.target.files && e.target.files[0]){
let imageData = e.target.files[0];
const reader = new FileReader();
reader.onload = x => {
setValues({
...values,
imageData,
imageSource : x.target.result
})
}
reader.readAsDataURL(imageData)
}
else{
setValues({
...values,
imageData:null,
imageSource:''
})
}
}
const validate = () => {
let temp = {}
temp.customerName = values.customerName == "" ? false : true;
setErrors(temp)
return Object.values(temp).every(x => x == true)
}
const resetForm = () => {
setValues(initialFieldValues)
document.getElementById('image-uploader').value = null;
}
const handleFormSubmit = e => {
e.preventDefault()
if (validate()){
const formData = new FormData()
formData.append('orderID',values.orderID)
formData.append('date_time',values.date_time)
formData.append('status',values.status)
formData.append('status2',values.status2)
formData.append('pharmacyName',values.pharmacyName)
formData.append('customerName',values.customerName)
formData.append('patientName',values.patientName)
formData.append('patientAge',values.patientAge)
formData.append('address',values.address)
formData.append('email',values.email)
formData.append('teleNo',values.teleNo)
formData.append('customer_id',values.customer_id)
formData.append('pharmacy_id',myphmcy)
formData.append('image',values.image)
formData.append('imageData',values.ImageData)
addOrEdit(formData, resetForm)
alert("Your file is being uploaded!")
}
}
const applyErrorClass = field => ((field in errors && errors[field] == false) ? ' invalid-field' : '')
return (
<>
<div className="container text-center ">
<p className="lead"></p>
</div>
<form autoComplete="off" noValidate onSubmit={handleFormSubmit}>
<div className="card">
<div className="card-header text-center">Place Your Order Here</div>
<img src={values.imageSource} className="card-img-top"/>
<div className="card-body">
<div className="form-group">
<input type="file" accept="image/*" className="form-control-file" onChange={showPreview} id="image-uploader"/>
</div>
<div className="form-group">
<input type="datetime-local" className="form-control" placeholder="Date Time" name="date_time" value={values.date_time}
onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Enter the prescription items and qty" name="status" value={values.status} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="What are the symptoms?" name="status2" value={values.status2} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Pharmacy Name" name="pharmacyName" value={values.pharmacyName} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className={"form-control" + applyErrorClass('customerName')} placeholder="Your Name" name="customerName" value={values.customerName} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Patient Name" name="patientName" value={values.patientName} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Patient Age" name="patientAge" value={values.patientAge} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Delivery address" name="address" value={values.address} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Your Email" name="email" value={values.email} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Contact Number" name="teleNo" value={values.teleNo} onChange={ handleInputChange}/>
</div>
<div className="form-group text-center">
<button type="submit" className="btn btn-light">submit</button>
</div>
</div>
</div>
</form>
</>
)
}
The codes are so long. but I have uploaded them all for a better understanding of my issue.
I need to add div with three text box, and this div need to be added again and again when add branch button is clicked, Like wise how to do it and get value from it ?
my code:
<div className="form-group">
<label>Store Address</label>
<input type="text"
value={this.state.input.address1}
onChange={this.handleChange}
className="form-control code" name="address1" placeholder="Pincode" />
<span className="form-text" id="errtext">{this.state.errors.address1}</span>
</div>
<div className="form-group">
<div className="row">
<div className="col">
<input type="text" className="form-control code"
value={this.state.input.address2}
onChange={this.handleChange}
name="address2" placeholder="address" />
<span className="form-text" id="errtext">{this.state.errors.address2}</span>
</div>
<div className="col-md-auto">
<p style={{margin:"5px"}}>and</p>
</div>
<div className="col">
<SimpleModal className="form-control" mapFunctionHere={this.mapFunctionHere} />
</div>
</div>
</div>
<div className="form-group">
<input type="text"
className="form-control code"
value={this.state.MapAddr}
onChange={this.handleChange}
name="address3" placeholder="address" readOnly />
<span className="form-text" id="errtext">{this.state.errors.address3}</span>
</div>
It is the div code which to be added multiple times when i click :
<input type="button" className="addbranch" onClick={this.appendDiv} value="Add Branch" />
when add branch is clicked once the above div should appear once, if button is clicked twice then it should appear twice, I need to do like the above and validate the field and get values, how to do it ?
You could use a counter in your state which increases every time, when you click the button until the limit is reached.
import React, { Component } from 'react'
export default class App extends Component {
state = {
counter: 0,
values: [
{ address1: { value: '', error: ''}, address2: { value: '', error: '' }, address3: { value: '', error: '' } }
]
}
handleChangeInput = (counter, key, value) => {
const newValues = [...this.state.values]
newValues[counter][key].value = value
this.setState({ values: newValues })
}
renderBranch = counter => {
const { values } = this.state
return (
<>
<div className="form-group">
<label>Store Address</label>
<input type="text"
value={values[counter].address1.value}
onChange={event => this.handleChangeInput(counter, 'address1', event.target.value)}
className="form-control code"
name="address1"
placeholder="Pincode"
/>
<span className="form-text" id="errtext">
{values[counter].address1.error}
</span>
</div>
<div className="form-group">
<div className="row">
<div className="col">
<input type="text" className="form-control code"
value={values[counter].address2.value}
onChange={event => this.handleChangeInput(counter, 'address2', event.target.value)}
name="address2"
placeholder="address" />
<span className="form-text" id="errtext">
{values[counter].address2.error}
</span>
</div>
</div>
<div className="col-md-auto">
<p style={{margin:"5px"}}>and</p>
<div className="col">
<SimpleModal className="form-control" mapFunctionHere={this.mapFunctionHere}/>
</div>
</div>
</div>
<div className="form-group">
<input type="text"
className="form-control code"
value={values[counter].address3.value}
onChange={event => this.handleChangeInput(counter, 'address2', event.target.value)}
name="address3"
placeholder="address"
readOnly
/>
<span className="form-text" id="errtext">
{values[counter].address3.error}
</span>
</div>
</>
)
}
renderBranches = () => {
const { counter } = this.state
const result = []
for (let i = 0; i <= counter; i++) {
result.push(this.renderBranch(i))
}
return result
}
appendDiv = () => {
this.setState({
counter: this.state.counter + 1,
values: [
...this.state.values,
{ address1: { value: '', error: ''}, address2: { value: '', error: '' }, address3: { value: '', error: '' } }
]
})
}
render = () => {
return (
<>
{this.renderBranches()}
<input type="button" className="addbranch" onClick={this.appendDiv} value="Add Branch" />
</>
)
}
}
I am working on a quiz app project to learn to react. I came across a situation where I need to store incorrect options in a quiz question in an array. And later pass over the information to the database.
This is an example JSON format.
{
incorrect_answers:["Jeff Bezos","Satya Nadela","Bill Gates"] }
The incorrect answer is an array and the value needs to be inputted through separate text boxes for each incorrect option like this.
option input form
The part where I am stuck is appending them to the array here is my attempt.
export default class CreateQuiz extends Component{
constructor(props){
super(props);
this.onChangedIncorrectAnswer=this.onChangedIncorrectAnswer.bind(this);
this.onSubmit=this.onSubmit.bind(this);
this.state={
incorrect_answers:[]
}
}
onChangedIncorrectAnswer(e){
const option=e.target.value
this.setState({
incorrect_answers:[...this.state.incorrect_answers,option]
});
}
onSubmit(e){
e.preventDefault();
const quiz = {
incorrect_answers:this.state.incorrect_answers
}
console.log(quiz);
axios.post("http://localhost:3000/quizes",quiz)
.then(res=>console.log(res.data));
window.location='/quiz-list';
}
render(){
return (
<div>
<h3>Create New Quiz</h3>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Incorrect Option 1</label>
<input type="text"
required
className="form-control"
value={this.state.incorrect_answers[0]}
onChange={this.onChangedIncorrectAnswer}
/>
</div>
<div className="form-group">
<label>Incorrect Option 2</label>
<input type="text"
required
className="form-control"
value={this.state.incorrect_answers[1]}
onChange={this.onChangedIncorrectAnswer}
/>
</div>
<div className="form-group">
<label>Incorrect Option 3</label>
<input type="text"
required
className="form-control"
value={this.state.incorrect_answers[2]}
onChange={this.onChangedIncorrectAnswer}
/>
</div>
<div className="form-group">
<input type="submit" value="Submit Quiz" className="btn btn-primary"/>
</div>
</form>
</div>
)
}
}
But the form was not working as expected. When I enter content for the first option in the "Option 1" text box only the first character is stored remaining in "Option 2" and so on.
try this, this would work!!
export default class CreateQuiz extends Component {
constructor(props) {
super(props);
this.onChangedCorrectAnswer = this.onChangedCorrectAnswer.bind(this);
this.onChangedIncorrectAnswer = this.onChangedIncorrectAnswer.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
category: "",
correct_answer: "",
difficulty: "",
type: "",
question: "",
incorrect_answers: ["", "", ""]
};
}
onChangedCorrectAnswer(e) {
this.setState({
correct_answer: e.target.value
});
}
onChangedIncorrectAnswer(e, index) {
const option = e.target.value;
const { incorrect_answers } = this.state;
incorrect_answers[index] = option;
this.setState({
incorrect_answers
});
}
onSubmit(e) {
e.preventDefault();
const quiz = {
category: this.state.category,
correct_answer: this.state.correct_answer,
incorrect_answers: this.state.incorrect_answers,
difficulty: this.state.difficulty,
type: this.state.type,
question: this.state.question
};
console.log(quiz);
axios
.post("http://localhost:3000/quizes", quiz)
.then((res) => console.log(res.data));
window.location = "/quiz-list";
}
render() {
return (
<div>
<h3>Create New Quiz</h3>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Correct Answer</label>
<input
type="text"
required
className="form-control"
value={this.state.correct_answer}
onChange={this.onChangedCorrectAnswer}
/>
</div>
<div className="form-group">
<label>Incorrect Option 1</label>
<input
type="text"
required
className="form-control"
value={this.state.incorrect_answers[0]}
onChange={(e) => this.onChangedIncorrectAnswer(e, 0)}
/>
</div>
<div className="form-group">
<label>Incorrect Option 2</label>
<input
type="text"
required
className="form-control"
value={this.state.incorrect_answers[1]}
onChange={(e) => this.onChangedIncorrectAnswer(e, 1)}
/>
</div>
<div className="form-group">
<label>Incorrect Option 3</label>
<input
type="text"
required
className="form-control"
value={this.state.incorrect_answers[2]}
onChange={(e) => this.onChangedIncorrectAnswer(e, 2)}
/>
</div>
<div className="form-group">
<input
type="submit"
value="Submit Quiz"
className="btn btn-primary"
/>
</div>
</form>
</div>
);
}
}
Your array state is considered as one state only
Why don't you create state on the go when user do any change in the input.
create a state object like
this.state = {incorrect_answers: {}}
In your onChangedIncorrectAnswer
onChangedIncorrectAnswer(e){
const option=e.target.value;
const stateName = e.target.name;
this.setState({
incorrect_answers: {...this.state.incorrect_answers, [stateName]: option }
});
}
use your form element as add name as unique which will be used as state
<div className="form-group">
<label>Incorrect Option 1</label>
<input name="incorrect_answers_0" type="text" required className="form-control" value={this.state.incorrect_answers[incorrect_answers_0]}
onChange={this.onChangedIncorrectAnswer} />
</div>
use that object while saving
onSubmit(e){
let yourIncorrectAnswers = Object.values(this.state.incorrect_answers);
})
}