React.js inserting multiple states into one main state - javascript

I'm trying to make an app where an 'Owner' can have multiple 'cars', I have my App.js file where the Owner enters there name and can enter there car details ('Car name' and 'Car Type'), a Owner can have multiple Cars, and when they click 'Add car entry' a Component where they enter there car details called 'OwnersCars' is repeated. Like so
to
If an owner fills out the input boxes in this component (For X amount of cars) then clicks 'Save Owner' i want the owner aswell as a list of all there cars to be saved into one State.
Currently i have my app.js file like this (count is used to know the number of OwnersCars divs)
import './App.css';
import React, {useState, useRef} from 'react';
import OwnersCars from './ownersCars';
function App() {
const [count, setCount] = useState(1)
const [OwnerInput, SetOwnerInput] = useState({
id: "",
Name: "",
cars: []
});
const [newCarInput, SetnewCarInput] = useState({
id: "",
Type: "",
CarName: ""
});
const removeDiv = () => {
//console.log('sw\nag')
setCount(count - 1)
}
const repeatDiv = () => {
//console.log('sw\nag')
setCount(count + 1)
}
const displayCarInput = (e) => {
//console.log(count, "<--key")
return ( ([...Array(count)].map((e, i) => <OwnersCars onAddNameCar={addNewCarNameHandler} onAddTypeCar={addNewCarTypeHandler}></OwnersCars> )))
}
const displayRemove = (e) =>{
if (count > 1) {
return (<button className='removeAnimalButton' onClick={removeDiv}> <dt> Remove Last Animal Entry</dt></button>)
}
}
const NameHandler = (e) => {
//console.log(e.target.value)
SetOwnerInput((prevState) => {
return { ...prevState, Name: e.target.value };
});
}
const submitHandler = (event) => {
event.preventDefault();
const value = Math.random().toString()
const OwnerData = {
id: value,
Name: OwnerInput.Name,
cars: [newCarInput]
};
console.log(OwnerData, "<--- ownerdata with cars data");
}
const addNewCarNameHandler = (values) => {
//console.log(values, "<---5")
SetnewCarInput((prevState) => {
return { ...prevState, CarName: values };
});
};
const addNewCarTypeHandler = (values) => {
//console.log(values, "<---5")
SetnewCarInput((prevState) => {
return { ...prevState, Type: values };
});
};
return (
<div>
<div>
<div>
<label for="exampleInputPassword1"></label>
<button onClick={submitHandler} ><dt>Save Owner</dt></button>
</div>
</div>
<hr/>
<div className="wrapper">
<div class="new-owner-div">
<h5>Owner</h5>
<hr/>
<form>
<div>
<input type="name" id="exampleInputClinic" placeholder="Owner Name" onChange={NameHandler}/>
</div>
</form>
</div>
<div class="new-owner-div-2">
<h5>Owners Cars</h5>
<hr/>
{displayCarInput()}
<div>
<button onClick={repeatDiv}> <dt> Add Car Entry</dt></button>
{displayRemove()}
</div>
</div>
</div>
</div>
);
}
export default App;
and i have my ownersCars.js file with the OwnersCars component like this
import React, {useState, useRef} from 'react';
function OwnersCars(props) {
const CarNameHandler = (e) => {
console.log(e.target.value)
props.onAddNameCar(e.target.value)
}
const CarTypeHandler = (e) => {
console.log(e.target.value)
props.onAddTypeCar(e.target.value)
}
return(
<div>
<div>
<div>
<h3>Car name</h3>
<span></span>
<h3>Type</h3>
</div>
<div>
<div>
<input placeholder="Car Name" onChange={CarNameHandler}/>
</div>
<span class="span1-box"></span>
<div class="height">
<input class="input-box-OA-2" placeholder="Car Type" onChange={CarTypeHandler}/>
</div>
<span class="span1-box"></span>
</div>
</div>
</div>
)
}
export default OwnersCars
but when i click save user it only saves the latest car entry!
Would anyone be able to help?
Sorry for the mess and lack of css i removed a bunch of things from the original code so it was easier to follow on StackOverflow. Also im fairly new to react so im sure theres alot of things that need to be changed for this to work.

You need to push to owner cars, every time you add a new car. Please find the code below for App.js changes. check repeatDiv. Similarly, you need to pop from cars the particular car with remove div which I leave it to you
import React, { useState, useRef } from "react";
import OwnersCars from "./Owner";
function App() {
const [count, setCount] = useState(1);
const [OwnerInput, SetOwnerInput] = useState({
id: "",
Name: "",
cars: []
});
const [newCarInput, SetnewCarInput] = useState({
id: "",
Type: "",
CarName: ""
});
const removeDiv = () => {
//console.log('sw\nag')
setCount(count - 1);
};
const repeatDiv = () => {
//console.log('sw\nag')
OwnerInput.cars.push(newCarInput);
setCount(count + 1);
};
const displayCarInput = (e) => {
//console.log(count, "<--key")
return [...Array(count)].map((e, i) => (
<OwnersCars
onAddNameCar={addNewCarNameHandler}
onAddTypeCar={addNewCarTypeHandler}
></OwnersCars>
));
};
const displayRemove = (e) => {
if (count > 1) {
return (
<button className="removeAnimalButton" onClick={removeDiv}>
{" "}
<dt> Remove Last Animal Entry</dt>
</button>
);
}
};
const NameHandler = (e) => {
//console.log(e.target.value)
SetOwnerInput((prevState) => {
return { ...prevState, Name: e.target.value };
});
};
const submitHandler = (event) => {
event.preventDefault();
const value = Math.random().toString();
const OwnerData = {
id: value,
Name: OwnerInput.Name,
cars: OwnerInput.cars
};
console.log(OwnerData, "<--- ownerdata with cars data");
};
const addNewCarNameHandler = (values) => {
//console.log(values, "<---5")
SetnewCarInput((prevState) => {
return { ...prevState, CarName: values };
});
};
const addNewCarTypeHandler = (values) => {
//console.log(values, "<---5")
SetnewCarInput((prevState) => {
return { ...prevState, Type: values };
});
};
return (
<div>
<div>
<div>
<label for="exampleInputPassword1"></label>
<button onClick={submitHandler}>
<dt>Save Owner</dt>
</button>
</div>
</div>
<hr />
<div className="wrapper">
<div class="new-owner-div">
<h5>Owner</h5>
<hr />
<form>
<div>
<input
type="name"
id="exampleInputClinic"
placeholder="Owner Name"
onChange={NameHandler}
/>
</div>
</form>
</div>
<div class="new-owner-div-2">
<h5>Owners Cars</h5>
<hr />
{displayCarInput()}
<div>
<button onClick={repeatDiv}>
{" "}
<dt> Add Car Entry</dt>
</button>
{displayRemove()}
</div>
</div>
</div>
</div>
);
}
export default App;
And the output with cars saved

Related

I need to re render state and make onChange operator work on input element

I am trying to edit the input box in a table in a CRUD manner ,but I am unable to update the state and make the input box read-writable.
I am using redux-toolkit to dispatch action for fetching the employee list from json-server.
In the middleware redux-saga is used.
The link to git hub repo:https://github.com/grbknr1996/employee-manager
import React, { useEffect, useState } from "react";
import "./Home.scss";
import { useDispatch, useSelector } from "react-redux";
import { sagaActions } from "../redux/sagaActions";
import { AiOutlineDelete, AiOutlineEdit } from "react-icons/ai";
import { MdDone } from "react-icons/md";
import { FormikBag, useFormik } from "formik";
const Home: React.FC = () => {
const dispatch = useDispatch();
const employees = useSelector((state: any) => state.employees.employees);
const [edit, setEdit] = useState({ edit: false, id: null as any });
const [editInfo, setEditInfo] = useState(employees);
const initialValues = {
id: "",
name: "",
designation: "",
address: "",
};
const formik = useFormik({
initialValues: initialValues,
onSubmit: (values) => {
console.log(JSON.stringify(values, null, 2));
},
});
useEffect(() => {
dispatch({ type: sagaActions.FETCH_EMPLOYEES_SAGA });
}, []);
useEffect(() => {
setInfo();
}, [employees]);
const setInfo = () => setEditInfo(employees);
const handleEdit = (e: React.MouseEvent, index: number) => {
console.log(index);
setEdit({ edit: true, id: index });
};
const handleDelete = (e: React.MouseEvent, index: number) => {
dispatch({ type: sagaActions.DELETE_EMPLOYEE_SAGA, id: index });
};
const toggleEdit = (e: React.FormEvent) => {
console.log("edit toggle");
setEdit({ edit: false, id: null as any });
};
const handleEditChange = (e: any, index: number) => {
setEditInfo(
employees.map((emp: any) =>
emp.id === index ? (emp.name = e.target.value) : emp
)
);
};
return (
<div className="main-container">
<div className="table-container">
<div className="table-row heading">
<div className="row-item">ID</div>
<div className="row-item">NAME</div>
<div className="row-item">DESIGNATION</div>
<div className="row-item">ADDRESS</div>
<div className="row-item">ACTIONS</div>
</div>
{editInfo.map((emp: any, index: number) =>
edit.edit && edit.id === index ? (
<>
<form>
<div className="table-row">
<div className="row-item" key={emp.id}>
{emp.id}
</div>
<input
className="row-item"
name="name"
value={emp.name}
onChange={(e) => handleEditChange(e, index)}
/>
<input className="row-item" value={emp.designation} />
<input className="row-item" value={emp.address} />
<div className="row-item">
<button type="submit">
<MdDone
className="changeColor done"
onClick={(e) => toggleEdit(e)}
/>
</button>
</div>
</div>
</form>
</>
) : (
<div className="table-row">
<div className="row-item" key={emp.id}>
{emp.id}
</div>
<div className="row-item" key={emp.name}>
{emp.name}
</div>
<div className="row-item" key={emp.designation}>
{emp.designation}
</div>
<div className="row-item" key={emp.address}>
{emp.address}
</div>
<div className="row-item">
<AiOutlineEdit
className="changeColor edit"
onClick={(e) => handleEdit(e, index)}
/>
<AiOutlineDelete
className="changeColor delete"
onClick={(e) => handleDelete(e, emp.id)}
/>
</div>
</div>
)
)}
</div>
</div>
);
};
export default Home;
Any will help will be great .Thanks in advance :) .
Instead of value for the input, use defaultValue
<input className="row-item" name="name" defaultValue={emp.name} onChange={(e)=> handleEditChange(e, index)} />

Check if all checkboxes are checked and console.log("all checked") Javascript/React

The following component is meant to be a To-Do list. I got multiple checkboxes. As soon as all checkboxes are checked, a console.log("all checked") should appear.
My idea is to check if the todo.lengh === checked.length, but it doesn't work.
Problem: trying to console.log(checked.length) doesn't work either so there must be the problem.
Can someone help me how to reach the checked.length?
import React from 'react';
import { AiOutlinePlusCircle } from 'react-icons/ai';
import { useState } from 'react';
function Checkboxes() {
const [todo, setToDo] = React.useState('');
const [todos, setToDos] = React.useState([]);
const [checked, setChecked] = useState(false);
function handleToDoSubmit(e) {
e.preventDefault();
const newToDo = {
id: new Date().getTime(),
text: todo,
completed: false,
};
setToDos([...todos].concat(newToDo));
setToDo('');
}
function toggleCompleteToDo(id) {
const updatedToDos = [...todos].map((todo) => {
if (todo.id === id) {
todo.completed = !todo.completed;
}
return todo;
});
setToDos(updatedToDos);
}
function allChecked(checked) {
if (todo.length === checked.length) {
console.log('all checked');
}
}
return (
<div className="ToDoList">
<form className="goalInputToDo" onSubmit={handleToDoSubmit}>
<input
className="goalInput"
type="text"
onChange={(e) => setToDo(e.target.value)}
value={todo}
/>
<button className="AddGoalBtn" type="submit">
.
<AiOutlinePlusCircle size="2em" />
</button>
</form>
{todos.map((todo) => (
<div className="goalItem">
<div key={todo.id}>
<div>{todo.text}</div>
<input
type="checkbox"
onChange={() => {
toggleCompleteToDo(todo.id), allChecked(todo.checked);
}}
checked={todo.completed}
/>
</div>
</div>
))}
</div>
);
}
export default Checkboxes;
todo is a string, checked is a boolean, so I'm not sure how you wanted to use them to check if all the checkboxes are checked. What you could do instead is to check your todos array and check if every single item's completed prop is true.
You can use Array#every() to do this. It tests whether all elements in the array pass the test implemented by the provided function:
function allChecked() {
return todos.every(item => item.completed)
}
function App() {
const [todo, setToDo] = React.useState('');
const [todos, setToDos] = React.useState([{
id: new Date().getTime(),
text: 'First item',
completed: false,
}]);
function handleToDoSubmit(e) {
e.preventDefault();
const newToDo = {
id: new Date().getTime(),
text: todo,
completed: false,
};
setToDos(todos.concat(newToDo));
setToDo('');
}
function toggleCompleteToDo(id) {
const updatedToDos = todos.map((item) => {
if (item.id === id) {
item.completed = !item.completed;
}
return item;
});
setToDos(updatedToDos);
}
function allChecked() {
if (!todos.length) return false;
return todos.every((item) => item.completed);
}
return (
<div className="ToDoList">
<form className="goalInputToDo" onSubmit={handleToDoSubmit}>
<input
className="goalInput"
type="text"
onChange={(e) => setToDo(e.target.value)}
value={todo}
/>
<button className="AddGoalBtn" type="submit">
Add
</button>
</form>
{todos.map((item) => (
<div className="goalItem">
<div key={item.id}>
<input
type="checkbox"
onChange={() => {
toggleCompleteToDo(item.id), allChecked(item.checked);
}}
checked={item.completed}
/>
<span>{item.text}</span>
</div>
</div>
))}
<p>All checked: {allChecked() ? 'Yes' : 'No'}</p>
</div>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
<div id="root"></div>
<script src="https://unpkg.com/react/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>

Toggle single element in React

I have one question. I created contact form in react and I want to collapsed only single contact, which was clicked. Toggle is method which should collapse it. And colapse is state. My problem is that when I click it affect all contact and all are collapsed. How can I improve it?
ContactBook.js
import React, { Component } from "react";
import Contact from "../Contact/Contact";
import "./ContactBook.css";
class ContactBook extends Component{
constructor(props){
super(props);
this.state = {
colapse :true,
contacts: [
{
id: 1,
name: 'Propulsion Academy',
address: 'Zurich',
avatar: 'propulsion-academy-logo.png'
},
{
id: 2,
name: 'Propulsion Academy',
address: 'Luzern',
avatar: 'propulsion-academy-logo.png'
},
{
id: 3,
name: 'Propulsion Academy',
address: 'Munich',
avatar: 'propulsion-academy-logo.png'
},
],
};
}
toggle=()=>{
const doesShow = this.state.colapse;
this.setState({colapse: !doesShow});
}
deleteContact=(contactIndex)=>{
//with slice method we create copy of an array
const contacts =this.state.contacts.slice();
contacts.splice(contactIndex, 1);
this.setState({contacts: contacts})
}
//get name from input
addName = e =>{
this.setState({
name: e.target.value,
})
}
//get address from input
addAddress = e =>{
this.setState({
address: e.target.value,
})
}
//update state on button click
handleSubmit = (e) =>{
e.preventDefault()
if(this.state.name && this.state.address) {
this.setState(state =>{
const newContact = {
id: Math.max(...state.contacts.map(c => c.id))+1,
name: this.state.name,
address: this.state.address,
}
return{
contacts:[...state.contacts, newContact]
}
})
}
}
render() {
return (
<div className="contactBook">
<form className ="addContact" >
<p>New Contact</p>
<label id="name"><p>Name</p><input type='text' id="name" onChange={this.addName}/></label>
<label id="address"><p>Address:</p><input type='text' id="address" onChange={this.addAddress} /></label>
<input type='file' name='file' />
<button type='submit' onClick= {this.handleSubmit}>SUBMIT</button>
</form>
<div className="contacts">
{this.state.contacts.map((contact, index) =>
< Contact key={contact.id} contact={contact} delete={()=>this.deleteContact(index)} colapse={this.state.colapse} toggle={this.toggle}/>)
}
</div>
</div>
);
}
};
export default ContactBook;
Contact.js
import React from "react";
import "./Contact.css";
import avatar from '../assets/user.png'
const Contact = (props) =>{
return (
<div className = "col" >
<img src={avatar} alt="avatar" onClick={props.toggle}/>
{props.colapse === true ?
<div>
<p>Name: {props.contact.name}</p>
<p>Address: {props.contact.address}</p>
<button onClick={props.delete}> Delete </button>
</div> : null
}
</div>
)
};
export default Contact;
I recommend to you to move the collapse and his method to the Contact component it self like this :
const Contact = (props) =>{
[collapse,setCollapse] = useState(true)
return (
<div className = "col" >
<img src={avatar} alt="avatar" onClick{()=>setCollape(prev=>!prev)}/>
{collapse === true ?
<div>
<p>Name: {props.contact.name}</p>
<p>Address: {props.contact.address}</p>
<button onClick={props.delete}> Delete </button>
</div> : null
}
</div>
)
};
In this component I created a state that will manage the collapse for each of the component the render in the map.
further more, the prev give you the last value you submit and it's best practice to use the prev instead of just setCollapse(!collapse)
You have one function for all contacts, and since you use .map() they will all behave the same, since toggling one toggles the state which is used to render all individual contacts. The solution would be to pass the selected contact in your state so your app actually knows which one is to be rendered! Hopefully that makes sense!
Good luck and let us know how things work out!
This is it:
ContactBook.js
import React, { Component } from "react";
import Contact from "./Contact";
// import "./ContactBook.css";
class ContactBook extends Component {
constructor(props) {
super(props);
this.state = {
contacts: [
{
id: 1,
name: "Propulsion Academy",
address: "Zurich",
avatar: "propulsion-academy-logo.png",
colapse: true
},
{
id: 2,
name: "Propulsion Academy",
address: "Luzern",
avatar: "propulsion-academy-logo.png",
colapse: true
},
{
id: 3,
name: "Propulsion Academy",
address: "Munich",
avatar: "propulsion-academy-logo.png",
colapse: true
}
]
};
}
// toggle = () => {
// const doesShow = this.state.colapse;
// this.setState({ colapse: !doesShow });
// };
deleteContact = contactIndex => {
//with slice method we create copy of an array
const contacts = this.state.contacts.slice();
contacts.splice(contactIndex, 1);
this.setState({ contacts: contacts });
};
togglecontact = contactIndex => {
let contacts = this.state.contacts.slice();
contacts[contactIndex].colapse = !contacts[contactIndex].colapse;
this.setState({ contacts: contacts });
};
//get name from input
addName = e => {
this.setState({
name: e.target.value
});
};
//get address from input
addAddress = e => {
this.setState({
address: e.target.value
});
};
//update state on button click
handleSubmit = e => {
e.preventDefault();
if (this.state.name && this.state.address) {
this.setState(state => {
const newContact = {
id: Math.max(...state.contacts.map(c => c.id)) + 1,
name: this.state.name,
address: this.state.address
};
return {
contacts: [...state.contacts, newContact]
};
});
}
};
render() {
return (
<div className="contactBook">
<form className="addContact">
<p>New Contact</p>
<label id="name">
<p>Name</p>
<input type="text" id="name" onChange={this.addName} />
</label>
<label id="address">
<p>Address:</p>
<input type="text" id="address" onChange={this.addAddress} />
</label>
<input type="file" name="file" />
<button type="submit" onClick={this.handleSubmit}>
SUBMIT
</button>
</form>
<div className="contacts">
{this.state.contacts.map((contact, index) => (
<Contact
key={contact.id}
contact={contact}
togglecontact={() => this.togglecontact(index)}
delete={() => this.deleteContact(index)}
colapse={this.state.colapse}
toggle={this.toggle}
/>
))}
</div>
</div>
);
}
}
export default ContactBook;
contactbook.js
import React from "react";
// import "./Contact.css";
const Contact = props => {
let buffer;
props.contact.colapse === true
? (buffer = (
<div>
<p>Name: {props.contact.name}</p>
<p>Address: {props.contact.address}</p>
<button onClick={props.delete}> Delete </button>
</div>
))
: null;
return (
<div className="col">
<img
onClick={props.togglecontact}
src="data:image/jpeg;base64,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"
/>
{buffer}
</div>
);
};
export default Contact;
can check it at:https://stackblitz.com/edit/react-mrvrr1?file=src%2FContact.js
only Toggling single element.

Component getting re rendered multiple times in react hooks

Since I am pretty new to react hooks, I am unable to understand some part of the existing code why my component is re-rendering multiple times when the state of an attribute gets changed. Below is the component code. I have added console.log for better understanding.
import React, { useState, useRef } from 'react';
import api from '../api/api';
import { UPLOAD_DATA } from '../api/urls';
import Alert from '../layout/alerts/Alerts';
const StudentDetailsView = ({ symbol }) => {
console.log("inside StudentDetailsView");
const initialState = {
form: {
qualification: symbol.qualification,
profession: symbol.profession
}
};
const [loading, setLoading] = useState(false);
const [errors, setErrors] = useState(null);
const [editFlag, setEditFlag] = useState(false);
const [inputs, setInputs] = useState(initialState);
console.log("before dataUpdated");
const [dataUpdated, setDataUpdated] =useState(false);
console.log("after dataUpdated");
const formRef = useRef(null);
const handleCancel = () => {
setEditFlag(false);
setInputs(initialState);
};
const handleSubmit = (e) => {
console.log("inside handleSumbit");
const form = formRef.current;
e.preventDefault();
e.stopPropagation();
form.classList.add('was-validated');
if (form.checkValidity()) {
callback();
}
};
const callback = ()=> {
setLoading(true);
const formData = new FormData();
formData.append('model', new Blob([JSON.stringify(inputs.form)], {
type: 'application/json'
}));
api.multipartEdit(UPLOAD_DATA, formData)
.then(response => {
setInputs(inputs => ({
...inputs,
form: {
qualification: response.data.qualification,
profession: response.data.profession
}
}));
setErrors(null);
setDataUpdated(true);
})
.catch(error => {
setErrors(error);
})
.finally(() => {
setLoading(false);
setEditFlag(false);
});
}
const handleInputChange = (event) => {
event.persist();
setInputs(inputs => ({
...inputs,
form: {
...inputs.form,
[event.target.name]: event.target.value
}
}));
}
return (
<div>
{
errors &&
<Alert type={errors.type} title={errors.title} description={errors.description} id="alert" />
}
<div >
{editFlag ? (
<div >
</div>
) :
(<div className="edit">
<button type="button" onClick={() => setEditFlag(!editFlag)}>
Edit
</button>
</div>)
}
</div>
<div>
<form className="needs-validation" onSubmit={handleSubmit} ref={formRef} noValidate>
{
editFlag ? (<div className="update-cancel-button">
<button className="btn btn-primary" type="submit" >
{loading ? (
<div className="spinner-border uxf-spinner-border-sm">
<span className="sr-only">Loading...</span>
</div>) : 'Update'}
</button>
<button className="btn btn-secondary cancel-button" type="button" onClick={handleCancel}>Cancel</button>
</div>) : <div></div>
}
<dl className="uxf-dl uxf-dl-horizontal">
<dt>Name</dt>
<dd>{symbol.name}</dd>
<dt>Age</dt>
<dd>{symbol.age}</dd>
<dt>Qualification</dt>
{editFlag ?
(<dd>
<textarea className="form-control" name="qualification" id="qualification"
value={inputs.form.qualification}
onChange={handleInputChange}
maxLength="255"></textarea>
<div className="invalid-feedback">
Please provide a Qualification.
</div>
</dd>)
:
(<dd>{dataUpdated ? (inputs.form.qualification ? inputs.form.qualification : '-') : (symbol.qualification ? symbol.qualification : '-')}</dd>)
}
<dt>Profession</dt>
{editFlag ?
(<dd>
<textarea className="form-control" name="profession" id="profession"
value={inputs.form.profession}
onChange={handleInputChange}
minLength="1"
maxLength="1000"
required></textarea>
<div className="invalid-feedback">
Please provide a Profession.
</div>
</dd>)
:
(<dd>{dataUpdated ? inputs.form.profession : symbol.profession}</dd>)
}
</dl>
</form>
</div>
</div>
);
}
export default StudentDetailsView;
Since my component is getting re-rendered, my state values which are getting set in the code (eg, dataUpdated) are getting updated with the default value again and again. How do I prevent this from happening? Please see the below images for better understanding.
(Showing the mockup of the edit component as the actual data was showing in the actual edit component)
I have clicked the edit button once and then clicked the cancel button once and this is the console log got generated.
Please uase spread operators on initializing or settingup states
const [inputs, setInputs] = useState({...initialState});
const handleCancel = () => {
setEditFlag(false);
setInputs({...initialState});
};

How I can add multiple same fields form in reactJS?

I want to add multiple persons dynamically in my form. Like I have Person 1 username and email then when I click Add Person it should make same fields for person 2 on the same page. When I click the Submit button it should give me the object of all persons.
App.js
import './App.css';
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class App extends Component {
state = {
fields:[]
};
addPerson() {
this.setState({fields:[...this.state.fields, ""]})
};
handleChange(e, index) {
this.state.fields[index] = e.target.value;
this.setState({fields: this.state.fields});
}
handleSubmit(e) {
console.log(this.state,"$$")
}
render() {
return (
<div className="App">
<header className="App-header">
<div>
<h1>The Form</h1>
{
this.state.fields.map((field, index) => {
return(
<div key={index}>
<input onChange={(e)=>this.handleChange(e, index)} value={field}/>
</div>
)
}
)
}
<button onClick={(e) => this.addPerson(e)}>Add Person</button>
<button onClick={(e) => this.handleSubmit(e)}>Submit</button>
</div>
</header>
</div>
)
}
}
I want my state would be like this...
state = {
fields:[
{
id: 1,
name: 'Max',
email: 'max.max#max.in'
}
]
};
Demo of my current page.
This is my solution codesandbox
You need to have two inputs, for email and name, and depending on which input is updated, update the value of person in array.
import React, { Component } from "react";
import "./styles.css";
export default class App extends Component {
state = {
fields: []
};
addPerson() {
const newPerson = {
id: Math.random(),
name: "",
email: ""
};
this.setState({ fields: [...this.state.fields, newPerson] });
}
handleChange(e, index) {
const fieldsCopy = [...this.state.fields];
fieldsCopy.forEach(item => {
if (item.id === index) {
item[e.target.name] = e.target.value;
}
});
this.setState({ fields: fieldsCopy }, () => console.log(this.state.fields));
}
handleSubmit(e) {
console.log(this.state, "$$");
}
render() {
return (
<div className="App">
<header className="App-header">
<div>
<h1>The Form</h1>
{this.state.fields.map(field => {
return (
<div key={field.id}>
<input
onChange={e => this.handleChange(e, field.id)}
name="name"
/>
<input
onChange={e => this.handleChange(e, field.id)}
name="email"
/>
</div>
);
})}
<button onClick={e => this.addPerson(e)}>Add Person</button>
<button onClick={e => this.handleSubmit(e)}>Submit</button>
</div>
</header>
</div>
);
}
}
Edited:
Here is my version of it:
import './App.css';
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class App extends Component {
index = 0;
state = {
fields: []
};
handleChange(e, idx) {
const { name, value } = e.target;
this.setState(state => {
return state.fields[idx][name] = value;
});
}
addPerson = () => {
const person = { id: this.index, name: '', email: '' };
this.index++;
this.setState({ fields: [ ...this.state.fields, person ] })
}
handleSubmit = () => {
console.log(this.state.fields);
}
render() {
const { fields } = this.state;
return (
<div className="App">
<header className="App-header">
<div>
<h1>The Form</h1>
{fields.length
? fields.map((field, idx) => (
<div key={idx}>
<label>Name:</label>
<input type="text" onChange={(e)=>this.handleChange(e, idx)} name="name" value={field.name}/>
<label>Email:</label>
<input type="email" onChange={(e)=>this.handleChange(e, idx)} name="email" value={field.email}/>
</div>
))
: null
}
<button onClick={this.handleSubmit}>Submit</button>
<button onClick={() => this.addPerson()}>Add Person</button>
</div>
</header>
</div>
)
}
}
If you are using the person id as unique identifier outside this component's state, I would suggest using some id generator library like uuid.
I hope this helps!

Categories

Resources