How to add component onclick on multiple times in react? - javascript

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" />
</>
)
}
}

Related

Is there a way I can make a react drop down rendered alone

Devs in the house.
I'm working on a little project where a user enters his info and on clicking of an action button, a list is to be dropped down for various options. My issue is that when a button is clicked, all the to-be-dropped-list are equally affected. and I only want the one whose button was clicked to be dropped down.
import React, { useState,useEffect } from 'react'
import Action from './action'
const Form = () => {
document.title = 'Contact Form'
const [action, setAction] = useState(false)
const [people, setPeople] = useState([])
const [user, setUser] = useState({firstName: '', lastName: '', email: '', phone: '',single: '', married: '', student: '', employed: ''})
const handleChange=(e)=>{
const name = e.target.name
const value =e.target.value
setUser({...user, [name]: value})
// console.log(`Name: ${name} Value: ${value}`);
}
var loadFile = function(e){
var image = document.getElementById('output')
image.src = URL.createObjectURL(e.target.files[0])
}
const handleSubmit = (e)=>{
console.log(12345);
e.preventDefault()
if(user.firstName && user.lastName && user.phone){
const newUser = {...user, time: new Date().getTime().toString()}
setPeople([...people, newUser])
// clearing fields
setUser({firstName: '', lastName: '', phone: '', email: ''})
// console.log("People :",people,"User: ",user);
// img
}else{
console.log("Field cannot be empty");
}
}
const showPpt =(e, index, time)=>{
console.log('sibling',e.target.nextElementSibling);
console.log(people);
people.forEach((a,i)=>{
if (a.time === time) {
console.log(e.target.parentElement.parentElement.parentElement);
console.log('old time',a.time, 'btn time',time);
if (action === true) {
setAction(false)
}else if (action === false) {
setAction(true)
}
}
else{
console.log('absent');
console.log('old time',a.time, 'btn time',time);
// setAction(false)
}
})
// if(time === people[index].time){
// console.log('Index ',index,e.target.parentElement.parentElement.parentElement);
// console.log('fetch time',people[index].time, 'current time', time);
// }
}
return (
<section>
<section className="head">
<h1>hello people</h1>
<form>
<section className="inp name">
<label htmlFor="firstName">First Name</label>
<input type="text" name="firstName" placeholder='First name' id="firstName" onChange={handleChange} value={user.firstName} />
<label htmlFor="lastName">Last Name</label>
<input type="text" name="lastName" placeholder='Last name' id="lastName" onChange={handleChange} value={user.lastName}/>
</section>
<section className="inp email">
<label htmlFor="email">Email</label>
<input type="email" name="email" id="email" onChange={handleChange} value={user.email} />
<label htmlFor="phone">Phone</label>
<input type="number" name="phone" id="phone" onChange={handleChange} value={user.phone}/>
</section>
{/* <section className='inp image'>
<label htmlFor="file">Image</label>
<input type="file" accept="image/gif, image/jpeg, image/png" name="image" id="file" style={{display: 'none'}} onChange={loadFile} value={user.fileData}/>
<img id='output' style={{width: '5rem'}} />
</section> */}
<section className="inp Address"></section>
<div className="radio-btn">
<section className="inp mrg-status">
<h4>Relationship</h4>
<div className="cont">
<label htmlFor="single">Single</label>
<input type="radio" name="relationship" id="single" onChange={handleChange} value={"single"}/>
<label htmlFor="married">Married</label>
<input type="radio" name='relationship' id='married' onChange={handleChange} value={"married"}/>
</div>
</section>
<section className="inp employment">
<h4>Employment</h4>
<div className="cont">
<label htmlFor="student">Student</label>
<input type="radio" name="employment" id="student" onChange={handleChange} value={"employed"} />
<label htmlFor="employed">Employed</label>
<input type="radio" name="employment" id="employed" onChange={handleChange} value={"employed"} />
</div>
</section>
</div>
<section className="submit">
<button className='btn' type='submit' onClick={handleSubmit}>Add Person</button>
</section>
</form>
</section>
<section className="main">
{people.map((data, index)=>{
const {firstName, lastName, email, phone, relationship, employment, time} = data
return <article className='user-contact' key={index} {...data}>
<article className="cont">
<div className="info">
<h4>Private information</h4>
<h4>Name: {firstName} {lastName}</h4>
<h4>Phone: {phone}</h4>
<h4>Email: {email}</h4>
<h4>Relationship status: {relationship}</h4>
<h4>Employment status: {employment}</h4>
</div>
<div className="action">
<button className='but' onClick={(e)=>showPpt(e, index, time)}>action</button>
{action && <Action />}
</div>
</article>
</article>
})}
</section>
</section>
)
}
export default Form
That's happen because you deal with an array of people with a single boolean state, which populate all the actions of all people.
I suggest you add an extra field for each element in people array which indicates the active/unactive action.
import React, { useState,useEffect } from 'react'
import Action from './action'
const Form = () => {
document.title = 'Contact Form'
const [action, setAction] = useState(false)
const [people, setPeople] = useState([])
const [user, setUser] = useState({firstName: '', lastName: '', email: '', phone: '',single: '', married: '', student: '', employed: ''})
const handleChange=(e)=>{
const name = e.target.name
const value =e.target.value
setUser({...user, [name]: value})
// console.log(`Name: ${name} Value: ${value}`);
}
var loadFile = function(e){
var image = document.getElementById('output')
image.src = URL.createObjectURL(e.target.files[0])
}
const handleSubmit = (e)=>{
console.log(12345);
e.preventDefault()
if(user.firstName && user.lastName && user.phone){
const newUser = {...user, time: new Date().getTime().toString(), active: false}
setPeople([...people, newUser])
// clearing fields
setUser({firstName: '', lastName: '', phone: '', email: ''})
// console.log("People :",people,"User: ",user);
// img
}else{
console.log("Field cannot be empty");
}
}
const showPpt =(e, index, time)=>{
let personIndex = people.findIndex(a => a.time === time);
setPeople((prev) => {
prev[personIndex].active = !prev[personIndex].active
return [...prev]
})
}
return (
<section>
<section className="head">
<h1>hello people</h1>
<form>
<section className="inp name">
<label htmlFor="firstName">First Name</label>
<input type="text" name="firstName" placeholder='First name' id="firstName" onChange={handleChange} value={user.firstName} />
<label htmlFor="lastName">Last Name</label>
<input type="text" name="lastName" placeholder='Last name' id="lastName" onChange={handleChange} value={user.lastName}/>
</section>
<section className="inp email">
<label htmlFor="email">Email</label>
<input type="email" name="email" id="email" onChange={handleChange} value={user.email} />
<label htmlFor="phone">Phone</label>
<input type="number" name="phone" id="phone" onChange={handleChange} value={user.phone}/>
</section>
{/* <section className='inp image'>
<label htmlFor="file">Image</label>
<input type="file" accept="image/gif, image/jpeg, image/png" name="image" id="file" style={{display: 'none'}} onChange={loadFile} value={user.fileData}/>
<img id='output' style={{width: '5rem'}} />
</section> */}
<section className="inp Address"></section>
<div className="radio-btn">
<section className="inp mrg-status">
<h4>Relationship</h4>
<div className="cont">
<label htmlFor="single">Single</label>
<input type="radio" name="relationship" id="single" onChange={handleChange} value={"single"}/>
<label htmlFor="married">Married</label>
<input type="radio" name='relationship' id='married' onChange={handleChange} value={"married"}/>
</div>
</section>
<section className="inp employment">
<h4>Employment</h4>
<div className="cont">
<label htmlFor="student">Student</label>
<input type="radio" name="employment" id="student" onChange={handleChange} value={"employed"} />
<label htmlFor="employed">Employed</label>
<input type="radio" name="employment" id="employed" onChange={handleChange} value={"employed"} />
</div>
</section>
</div>
<section className="submit">
<button className='btn' type='submit' onClick={handleSubmit}>Add Person</button>
</section>
</form>
</section>
<section className="main">
{people.map((data, index)=>{
const {firstName, lastName, email, phone, relationship, employment, time} = data
return <article className='user-contact' key={index} {...data}>
<article className="cont">
<div className="info">
<h4>Private information</h4>
<h4>Name: {firstName} {lastName}</h4>
<h4>Phone: {phone}</h4>
<h4>Email: {email}</h4>
<h4>Relationship status: {relationship}</h4>
<h4>Employment status: {employment}</h4>
</div>
<div className="action">
<button className='but' onClick={(e)=>showPpt(e, index, time)}>action</button>
{action && <Action />}
</div>
</article>
</article>
})}
</section>
</section>
)
}
export default Form

Uncaught TypeError: Cannot read properties of undefined (reading 'roll')

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) {
...
}

incorrect value of name attribute in event object

I read about using a single handler for multiple input fields. When I'm trying to use that approach -> using event.target.value, I am getting just the first letter of what I entered in the input. Can anyone please help me on what I am doing wrong.
class AddContactForm extends Component {
constructor(props) {
super(props);
this.name = "";
this.email = ""
this.phone = "";
}
componentDidUpdate() {
console.log("updated");
}
componentWillUnmount() {
console.log("unmounted");
}
componentWillMount() {
console.log("mounted");
}
handleOnChange(event) {
[event.target.name] = event.target.value;
console.log(event)
console.log(event.target.name);
console.log(event.target.value);
}
addContact() {
console.log("add");
this.props.addContactHandler({
name: this.name,
email: this.email,
phone: this.phone
});
}
render() {
return (
<React.Fragment>
<div className="mx-auto">
<div class="mb-3 row">
<label for="username" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input type="text" class="form-control" name='name' id="username" onChange={this.handleOnChange.bind(this)} />
</div>
</div>
<div class="mb-3 row">
<label for="mobileNumber" class="col-sm-2 col-form-label">Mobile Number</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="phone" id="mobileNumber" onChange={this.handleOnChange.bind(this)} />
</div>
</div>
<div class="mb-3 row">
<label for="emailId" class="col-sm-2 col-form-label">Email</label>
<div class="col-sm-10">
<input type="email" class="form-control" id="emailId" name="email" onChange={this.handleOnChange.bind(this)} />
</div>
</div>
<button className="btn btn-primary w-25" onClick={this.addContact.bind(this)}>Add</button>
</div>
</React.Fragment>
)
}
}
export default AddContactForm;
inside handle on change, the value of [event.target.name] always shows whatever was my first input alphabet. for If I am writong abcd ing input, ikt'll keep showing me a .
Where am I going wrong ?
The issue is with the first line of code in this function.
handleOnChange(event) {
[event.target.name] = event.target.value;
console.log(event)
console.log(event.target.name);
console.log(event.target.value);
}
That changes the HTML Input element name's first letter to the first letter of the input value you have typed.
Add with the following log
handleOnChange = (event) => {
[event.target.name] = event.target.value;
console.log(event);
console.log(event.target);
console.log(event.target.name);
console.log(event.target.value);
};
Solution
To change the class variable dynamically,
instead of
[event.target.name] = event.target.value;
use
this[event.target.name] = event.target.value;
You haven't set the state properly in both constructor and handleChange functions.
Try this
import React, { Component } from "react";
class AddContactForm extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
email: "",
phone: ""
};
}
componentDidUpdate() {
console.log("updated");
}
componentWillUnmount() {
console.log("unmounted");
}
componentWillMount() {
console.log("mounted");
}
handleOnChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
addContact() {
console.log("add");
console.log(this.state);
// this.props.addContactHandler(this.state);
}
render() {
return (
<React.Fragment>
<div className="mx-auto">
<div class="mb-3 row">
<label for="username" class="col-sm-2 col-form-label">
Name
</label>
<div class="col-sm-10">
<input
type="text"
class="form-control"
name="name"
id="username"
onChange={this.handleOnChange.bind(this)}
/>
</div>
</div>
<div class="mb-3 row">
<label for="mobileNumber" class="col-sm-2 col-form-label">
Mobile Number
</label>
<div class="col-sm-10">
<input
type="text"
class="form-control"
name="phone"
id="mobileNumber"
onChange={this.handleOnChange.bind(this)}
/>
</div>
</div>
<div class="mb-3 row">
<label for="emailId" class="col-sm-2 col-form-label">
Email
</label>
<div class="col-sm-10">
<input
type="email"
class="form-control"
id="emailId"
name="email"
onChange={this.handleOnChange.bind(this)}
/>
</div>
</div>
<button
className="btn btn-primary w-25"
onClick={this.addContact.bind(this)}
>
Add
</button>
</div>
</React.Fragment>
);
}
}
export default AddContactForm;
Check console logs
Code sandbox URL => https://codesandbox.io/s/friendly-feather-jcj99?file=/src/App.js:0-2241
you should render this component for this work can use the state and set state ...
class AddContactForm extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
email: "",
phone: ""
}
}
componentDidUpdate() {
console.log("updated");
}
componentWillUnmount() {
console.log("unmounted");
}
componentWillMount() {
console.log("mounted");
}
handleOnChange(event) {
this.setState({ [event.target.name]: event.target.value })
console.log(event)
console.log(event.target.name);
console.log(event.target.value);
}
addContact() {
console.log("add");
this.props.addContactHandler(this.state);
}
render() {
return (
<React.Fragment>
<div className="mx-auto">
<div class="mb-3 row">
<label for="username" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input type="text" class="form-control" name='name' id="username" onChange={this.handleOnChange.bind(this)} />
</div>
</div>
<div class="mb-3 row">
<label for="mobileNumber" class="col-sm-2 col-form-label">Mobile Number</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="phone" id="mobileNumber" onChange={this.handleOnChange.bind(this)} />
</div>
</div>
<div class="mb-3 row">
<label for="emailId" class="col-sm-2 col-form-label">Email</label>
<div class="col-sm-10">
<input type="email" class="form-control" id="emailId" name="email" onChange={this.handleOnChange.bind(this)} />
</div>
</div>
<button className="btn btn-primary w-25" onClick={this.addContact.bind(this)}>Add</button>
</div>
</React.Fragment>
)
}
}
export default AddContactForm;
If you want use a generic input change handler for all inputs you can get the name and value from event.target. Then create an object from each of them like {name]: value} to pass to this.setState.
handleOnChange = (event) => {
const { name, value } = event.target;
this.setState({
[name]: value
});
}
class AddContactForm extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
email: "",
phone: ""
};
}
handleOnChange = (event) => {
const { name, value } = event.target;
this.setState({
[name]: value
});
};
render() {
return (
<React.Fragment>
<label>
Name
<input
type="text"
name="name"
onChange={this.handleOnChange}
value={this.state.name}
/>
</label>
<label>
Mobile Number
<input
type="text"
name="phone"
onChange={this.handleOnChange}
value={this.state.phone}
/>
</label>
<label>
Email
<input
type="email"
name="email"
onChange={this.handleOnChange}
value={this.state.email}
/>
</label>
<pre>{JSON.stringify(this.state, null, 2)}</pre>
</React.Fragment>
);
}
}
export default AddContactForm;
If all of your fields are identical, you can even dynamically render them by mapping over the keys of the state object:
class AddContactForm extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
email: "",
phone: ""
};
}
handleOnChange = (event) => {
const { name, value } = event.target;
this.setState({
[name]: value
});
};
render() {
return (
<React.Fragment>
{Object.keys(this.state).map((key) => (
<label>
{key}
<input
type="text"
name={key}
onChange={this.handleOnChange}
value={this.state[key]}
/>
</label>
))}
<pre>{JSON.stringify(this.state, null, 2)}</pre>
</React.Fragment>
);
}
}
export default AddContactForm;

Adding array elements through multiple form fields in React

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);
})
}

Not changing values of form reactjs

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}>
// ...

Categories

Resources