Toggle single element in React - javascript

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.

Related

React.js inserting multiple states into one main state

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

how can i clean the input value after i click the button?

the below code works fine, but it has a small issue, it did not clean an input field value when I click the button, so I have tried to put a code this.setState({ name: ''}) into nameChangedHandler that make this input value back to empty, but it does not work and will lock the input value to empty, and then you could not type any data into this input value.
Does it work by using Component Lifecycle?
class AddPerson extends Component {
state = {
name: '',
age: '',
};
nameChangedHandler = event => {
this.setState({ name: event.target.value });
};
ageChangedHandler = event => {
this.setState({ age: event.target.value });
};
render() {
return (
<div className="AddPerson">
<input
type="text"
placeholder="Name"
onChange={this.nameChangedHandler}
value={this.state.name}
/>
<input
type="number"
placeholder="Age"
onChange={this.ageChangedHandler}
value={this.state.age}
/>
<button onClick={() => this.props.personAdded(this.state.name, this.state.age)}>
Add Person
</button>
</div>
);
}
}
export default AddPerson;
You clean the name in the button's onClick handler:
<button onClick={() => {
this.props.personAdded(this.state.name, this.state.age);
this.setState({ name: '' });
}}>
Add Person
</button>
class AddPerson extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
age: ""
};
}
nameChangedHandler = (event) => {
this.setState({ name: event.target.value });
};
ageChangedHandler = (event) => {
this.setState({ age: event.target.value });
};
handleSubmit = () => {
this.props.personAdded(this.state.name, this.state.age);
this.setState({
name: "",
age: ""
});
};
render() {
return (
<div className="AddPerson">
<input
type="text"
placeholder="Name"
onChange={this.nameChangedHandler}
value={this.state.name}
/>
<input
type="number"
placeholder="Age"
onChange={this.ageChangedHandler}
value={this.state.age}
/>
<button onClick={this.handleSubmit}>Add Person</button>
</div>
);
}
}
export default AddPerson;

Unable to pass React form data from a Child component to a Parent Component

I'm trying to pass data from a React form in a Child component to a Parent component, but i'm missing something.
When i pass the data from within the parent component only, everything works fine. However i would really like to learn how to use several components.
I'm new to React and i would really appreciate help from someone.
This is my Parent Component:
import React from 'react';
import CalculateIMC from './calculateIMC'
import Results from './results';
import '../css/Form.css';
class App extends React.Component{
constructor(props) {
super(props)
this.state = {
name: "",
height: "",
weight: "",
bmi: "",
};
this.calBmi = this.calBmi.bind(this);
}
calBmi = () => {
const { height, weight } = this.state;
const calcBmi = (weight / (height / 100) ** 2).toFixed(2);
const bmiClass = this.getBmi(calcBmi);
this.setState({isSubmitted: true})
this.setState({
bmi: calcBmi,
bmiClass : bmiClass
})
}
getBmi = (bmi) => {
if(bmi < 18.5) {
return "Underweight";
}
if(bmi >= 18.5 && bmi < 24.9) {
return "Normal weight";
}
if(bmi >= 25 && bmi < 29.9) {
return "Overweight";
}
if(bmi >= 30) {
return "Obesity";
}
}
clearAll = () => {
console.log("test");
this.setState({
name: "",
height: "",
weight: "",
bmi: ""
});
};
render() {
return (
<div className="App">
<CalculateIMC calBmi={this.calBmi}/>
{this.state.isSubmitted && <Results {...this.state}/>}
</div>
);
}
}
export default App
Child component
import React from 'react';
class CalculateIMC extends React.Component{
constructor(props) {
super(props)
this.state = {
name: "",
height: "",
weight: "",
bmi: "",
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit = (e) => {
e.preventDefault()
this.props.calBmi()
}
render() {
return (
<div className="container">
<form className="card-body" onSubmit={e => this.handleSubmit(e)}>
<div className="field">
<div className="two fields">
<div className="field">
<label>Nom : </label>
<input type="text" required placeholder="Saisir votre nom" value={this.state.name} onChange={e => this.setState({ name: e.target.value })}/>
</div>
<div className="field">
<label>Taille : </label>
<input type="number" required placeholder="Taille en cms" value={this.state.height} onChange={e => this.setState({ height: e.target.value })}/>
</div>
<br />
<div className="field">
<label>Poids : </label>
<input type="number" required placeholder="Poids en Kgs" value={this.state.weight} onChange={e => this.setState({ weight: e.target.value })}/>
</div>
</div>
<button type="submit" className="ui button" tabIndex="1">Calcul</button>
<button className="ui button" tabIndex="0" onClick={this.props.clearAll}>Effacer</button>
</div>
</form>
</div>
)
}
}
export default CalculateIMC
You are not passing the child's form data to the parent, instead you are just calling the callback without parameters, that's the problem.
On the child component you should do this (or similar):
handleSubmit = (e) => {
e.preventDefault();
this.props.calBmi(this.state);
}
On the parent:
calBmi = (childFormData) => {
const { height, weight } = childFormData;
const calcBmi = (weight / (height / 100) ** 2).toFixed(2);
const bmiClass = this.getBmi(calcBmi);
this.setState({isSubmitted: true}); // you can merge this setState with below one
this.setState({
bmi: calcBmi,
bmiClass : bmiClass
})
}
You need to update your calBmi() prop to have the form values as a parameter:
parent:
calBmi = (formParameters) => {
// do something with formParameters
}
child:
handleSubmit = (e) => {
e.preventDefault()
this.props.calBmi({
name: this.state.name,
height: this.state.height,
weight: this.state.weight,
}
})
}
I suggest you also change its name calBmi => onFormSubmit

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!

i want to use 2 function in onChange

import React, { Component } from "react"; import { Link } from
"react-router-dom";
const emailRegx = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
export default class ChefInfo extends Component { constructor(props) { super(props); this.state = { eInput: "",
small: "" };
-
----------
---------
}
handleChange = e => {
this.setState({
eInput: e.target.value
});
};
emailTest = () => {
if (emailRegx.test(this.state.eInput) === false) {
this.setState({
small: "your email is inccorect"
});
} else {
this.setState({
small: ""
});
}
};
render() {
return (
<div className="big-parent">
<form>
<div className="input">
<label>
<strong>E-mail</strong>
</label>
<input
type="email"
className="input-filed"
onChange={() => { //here the problem
this.handleChange();
this.emailTest();
}}
/>
<small className="small">{this.state.small}</small>
</div>
</form>
<a href="#" className="btn btn-dark button">
<strong>READY</strong>
</a>
</div>
);
} }
Your handlechange use event object parameter.
So you should pass event object.
onChange={(e) => { //here the problem
this.handleChange(e);
this.emailTest();
}}
But in this case, you don't need to use two function.
This is enough.
handleChange = (e) => {
this.setState({
eInput : e.target.value,
small : emailRegx.test(e.target.value) ? '' : "your email is incorrect"
})
};
You can refactor you code such that it looks like this. This make your render/template code look cleaner.
handleEvent(event) {
this.handleChange(event);
this.emailTest();
}
.
.
.
<input
type="email"
className="input-filed"
onChange={this.handleEvent}
/>

Categories

Resources