How do I change the state between inputs? - reactjs

I am trying to make an application that calculates tax and discounts, but I could not figure out how to establish communication between states. Since the change between inputs will be continuous, I gave the onkeyup event for each inbox. Every time this event is called, the called function must do the necessary actions. For example, when I enter the quantity and unitPrice values, it will multiply the two values and assign them to the goodsServiceAmt value. The application also has the ability to add and delete rows. The calculations of each line should be in itself. I tried to do it with the handleQuantity function, the state is updating but the value in the input does not change.
const App = () => {
const [mainData, setMainData] = useState([
{
id: 1,
quantity: "",
unitPrice: "",
discRate: "",
discAmt: "",
kdvAmt: "",
kdvRate: "",
goodsServiceAmt: "",
},
]);
const deleteRow = (e, i) => {
e.preventDefault();
const newArr = [...mainData];
console.log(i);
newArr.splice(i, 1);
setMainData(newArr);
};
const handleChangeInput = (e, i) => {
const values = [...mainData];
values[i][e.target.name] = e.target.value;
setMainData(values);
};
const addRow = () => {
const id = mainData.length ? mainData.length + 1 : 0;
console.log(id);
setMainData([
...mainData,
{
id,
quantity: "",
unitPrice: "",
discRate: "",
discAmt: "",
kdvAmt: "",
kdvRate: "",
goodsServiceAmt: "",
},
]);
};
const handleQuantity = (e, i) => {
const deger = [...mainData];
deger[i].unitPrice = e.target.value;
setMainData(deger);
};
return (
<div style={{ display: "flex", flexDirection: "column" }}>
{mainData.map((v, i) => (
<form key={i} style={{ display: "flex" }}>
<span style={{ width: "50px", height: "50px" }}>{v.id}</span>
<div className="input-cont">
<label htmlFor="quantity">quantity</label>
<input
type="text"
name="quantity"
value={mainData.quantity}
onChange={(e) => handleChangeInput(e, i)}
onKeyUp={(e) => handleQuantity(e, i)}
/>
</div>
<div className="input-cont">
<label htmlFor="unitPrice">unitPrice</label>
<input
type="text"
name="unitPrice"
value={mainData.unitPrice}
onChange={(e) => handleChangeInput(e, i)}
/>
</div>
<div className="input-cont">
<label htmlFor="discRate">discRate</label>
<input
type="text"
name="discRate"
value={mainData.discRate}
onChange={(e) => handleChangeInput(e, i)}
/>
</div>
<div className="input-cont">
<label htmlFor="discAmt">discAmt</label>
<input
type="text"
name="discAmt"
value={mainData.discAmt}
onChange={(e) => handleChangeInput(e, i)}
/>
</div>
<div className="input-cont">
<label htmlFor="kdvRate">kdvRate</label>
<input
type="text"
name="kdvRate"
value={mainData.kdvRate}
onChange={(e) => handleChangeInput(e, i)}
/>
</div>
<div className="input-cont">
<label htmlFor="kdvAmt">kdvAmt</label>
<input
type="text"
name="kdvAmt"
value={mainData.kdvAmt}
onChange={(e) => handleChangeInput(e, i)}
/>
</div>
<div className="input-cont">
<label htmlFor="goodsServiceAmt">goodsServiceAmt</label>
<input
type="text"
name="goodsServiceAmt"
value={mainData.goodsServiceAmt}
onChange={(e) => handleChangeInput(e, i)}
/>
</div>
<button onClick={(e) => deleteRow(e, i)}>delete</button>
</form>
))}
<button onClick={addRow}>add row</button>
<button onClick={() => console.log(mainData)}>clg</button>
</div>
);
};
export default App;

That happens because you're accessing the data in your inputs like an object but in your useState it's an array.
Change your use state to:
const [mainData, setMainData] = useState({
id: 1,
quantity: "",
unitPrice: "",
discRate: "",
discAmt: "",
kdvAmt: "",
kdvRate: "",
goodsServiceAmt: "",
});
Or change the value in your inputs to:
value={mainData[0].quantity}

Since your mainData is an array, when you are doing this assign to the input value mainData.quantity, you are actually passing an undefined, converting the input to an uncontrolled input. That uncontrolled input makes it look like you are getting the data from the state, but actually it isn't. So you need to change those calls to mainData[i].quantity to get the data properly.
And as a note, you can still use the onChange function to handle the quantity

Related

Im a React Beginner and need to know how to Lift up State from my form

So ive got a Booking component which returns a form which lets you choose number of Guests, Time, Occasion and a Date, but i need a way to lift the State up from this component so that i can use it in the future with an API.
I think i dont have the knowledge of the right Syntax or correct way of putting this component and its state. Ive been trying to Code it but the only thing i came up with is this:
import React from 'react'
import { useState } from 'react';
const availableTimes = [
{value: "17:00", text: "17:00"},
{value: "18:00", text: "18:00"},
{value: "19:00", text: "19:00"},
{value: "20:00", text: "20:00"},
{value: "21:00", text: "21:00"},
{value: "22:00", text: "22:00"},
]
const Guests = [
{value: "1", text: "1"},
{value: "2", text: "2"},
{value: "3", text: "3"},
{value: "4", text: "4"},
{value: "5", text: "5"},
{value: "6", text: "6"},
{value: "7", text: "7"},
{value: "8", text: "8"},
{value: "9", text: "9"},
]
const Occasion = [
{value: "Birthday", text: "Birthday"},
{value: "Anniversary", text: "Anniversary"},
]
const Select = ({value, options, onChange}) => {
return(
<select value={value} onChange={onChange}>
{options.map(option => {
return(<option key={option.value} value={option.value}>{option.text}</option>)
})}
</select>
)
}
const Bookingform = () => {
const[resdate, setResdate] = useState("");
const[guests, setGuests] = useState("");
const[occasion, setOccasion] =useState("");
const[restime, setRestime] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
const data = {
Reservation_Time: restime,
Occasion: occasion,
Guests: guests,
Reservation_Date: resdate
}
const json = JSON.stringify(data);
console.clear()
console.log(json)
setResdate("");
setRestime("");
setGuests("");
setOccasion("");
console.log("Form submitted!");
}
return (
<>
<div className='BookingForm'>
<form onSubmit={handleSubmit}>
<fieldset>
<div className='Field'>
<field>
<label htmlFor='res-date'>Choose Date</label>
<input id="res-date" type="date" placeholder='res-date' name='res-date' value={resdate} onChange={(e)=> setResdate(e.target.value)}/>
<label htmlFor='res-time'>Choose time:</label>
<Select id="restime" placeholder="restime" name="restime" value={restime} options={availableTimes} onChange={(e) => setRestime(e.target.value)} />
<label htmlFor="guests">Number of Guests</label>
<Select id="guests" placeholder='guests' name='guests' value={guests} options={Guests} onChange={(e) => setGuests(e.target.value)}/>
<label htmlFor="occasion">Occasion</label>
<Select id="occasion" placeholder='occasion' name="occasion" value={occasion} options={Occasion} onChange={(e) => setOccasion(e.target.value)}/>
</field>
</div>
<button type='submit'>Submit</button>
</fieldset>
</form>
</div>
</>
)
}
export default Bookingform
I think you should read this article:
https://dev.to/smeijer/simple-form-handling-in-react-o72
But a better way than what you have written is using FormData :
function MyForm() {
const handleSubmit = (event) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
const values = Object.fromEntries(data.entries());
if (!Number(values.age)) {
alert('Your age must be a number');
return;
}
console.log('submitting', values);
};
return (
<form onSubmit={handleSubmit}>
<h1>Hi!</h1>
<p>Enter your name:</p>
<input type="text" name="username" />
<p>Enter your age:</p>
<input type="text" name="age" />
<br /><br />
<input type="submit" />
</form>
);
}
Obviously a much better way would be to create a Hook for yourself using FormData which you can see a full example of it in the article I mentioned above.
So after some more research and reading the mentioned article i came up with this Solution. Is this a valid way of lifting the State of my Component to its Parent?
const formReducer= (state, event) => {
return {
...state,
[event.name]: event.value
}
}
const BookinForm = (props) => {
const [formData, setFormData] = useReducer(formReducer, {});
const [submitting, setSubmitting] = useState(false);
const handleSubmit = (e) => {
e.preventDefault();
setSubmitting(true);
props.onSubmit(formData);
setTimeout (() => {
setSubmitting(false);
}, 3000)
};
const handleChange = event => {
const isCheckbox = event.target.type === "checkbox";
setFormData({
name: event.target.name,
value: event.target.value,
value: isCheckbox ? event.target.checked : event.target.value,
});
}
return (
<div className="Wrapper">
<h1>Reserve a Table</h1>
{submitting && <div>You are submitting the following:
<ul>
{Object.entries(formData).map(([name, value]) => (
<li key={name}><strong>{name}</strong>:{value.toString()}</li>
))}
</ul>
</div> }
<form onSubmit={handleSubmit}>
<fieldset>
<label>
<p>Name</p>
<input name="name" onChange={handleChange}/>
</label>
<label>
<p>Email</p>
<input name="email" onChange={handleChange}/>
</label>
<label>
<p>Telephone</p>
<input name="telephone" onChange={handleChange}/>
</label>
</fieldset>
<fieldset>
<label>
<p>Time</p>
<select name="Time" onChange={handleChange}>
<option>17:00</option>
<option>18:00</option>
<option>19:00</option>
<option>20:00</option>
<option>21:00</option>
<option>22:00</option>
</select>
</label>
<label>
<p>Date</p>
<input type="date" name="Time" onChange={handleChange}/>
</label>
<label>
<p>Guests</p>
<input type="number" name="count" min="1" max="9" onChange={handleChange} step="1"/>
</label>
<label>
<p>Choose Occasion</p>
<select>
<option>Anniversary</option>
<option>Birthday</option>
</select>
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
</div>
)
}
it does function as intended as i can Console log the Data from my Form inside my Parent component if i tell it to use this function On Submit:
const getData = (data) => {
console.log("Coming from Parent", data)
};
Any advice still here or does this code look fine for more experienced people?

Submitting dynamically created form to database in reactJS

I am trying to insert the values through this dynamic form but I am only able to insert just one entry (even though I enter multiple forms) and the values that are getting inserted are 0 (in the database as well).
I am passing the index still I am not getting the desired output. The list looks like this:
List of the entered details
The previous entry was entered manually.
This is the CODE through which I submit values.
import React, { useEffect, useState } from "react";
import './roominfo.css';
import { ImBin } from 'react-icons/im';
import { useNavigate, useParams } from 'react-router-dom'
import RoomInfoService from '../services/RoomInfoService';
const RoomInfo = () => {
const [shared_no] = useState();
const [no_of_rooms] = useState();
const [rent_per_month] = useState();
const [vacancy] = useState();
const [total_capacity] = useState();
const [pg_fid, setpgfid] = useState();H
let handleChange = (i, e) => {
let newFormValues = [...formValues]; /** makes a copy of the current form values and assign it to newFormValues **/
newFormValues[i][e.target.name] = e.target.value;
setFormValues(newFormValues);
}
const navigate = useNavigate();
let addFormFields = () => {
setFormValues([...formValues, { shared_no: "", no_of_rooms: "", rent_per_month: "", vacancy: "",total_capacity: ""}])
};
let removeFormFields = (i) => {
let newFormValues = [...formValues];
newFormValues.splice(i, 1);
setFormValues(newFormValues)
}
const [formValues, setFormValues] = useState([{ shared_no: "", no_of_rooms: "", rent_per_month: "", total_capacity: "", vacancy: "" }])
/*const [settotal_no_of_rooms] = useState('1');
const [setrooms_entered] = useState('1');
const getTotalCapacity = () => {
return formValues.reduce((total,element) => {
return total + Number(element.shared_no) * Number(element.no_of_rooms);
}, 0);
};*/
const resetForm = () => setFormValues([{shared_no: "", no_of_rooms: "", rent_per_month: "", vacancy: "",total_capacity: ""}]);
const Total_no_of_rooms_entered = () => {
return formValues.reduce((total, element) => {
return total + Number(element.no_of_rooms);
}, 0);
};
const saveRoomInfo = (e) =>{
e.preventDefault();
const roominfo = [...formValues];
RoomInfoService.createRoomInfo(roominfo).then((response) => {
console.log(response.data);
navigate('/listroominfo');
}).catch(error =>{
console.log(error);
})
}
return (
<>
<div className='heading'>
<h1 style={{ marginBottom: '50px' }}>Enter Your PG Room Details</h1>
</div>
<form>
<div className="form-inline" style={{ marginBottom: '50px' }}>
{/*<label>Total of Rooms: </label>
<input type="number" name="total_no_of_rooms" style={{ width: '90px' }} onClick={(e) => settotal_no_of_rooms(e.target.value)} defaultValue={1} /> */}
<label>Rooms Entered:</label>
<div><input type="number" name="rooms_entered" value={Total_no_of_rooms_entered()} readOnly></input></div>
</div>
</form>
<div>
<form>
{formValues.map((element, index) => (
<div className="form-inline" key={index} style={{ borderBottom: '1px solid black' }}>
<label>Sharing Number</label>
<select id="shared_no" name="shared_no" title="no of shared rooms" value={element.shared_no || ""} onChange={e => handleChange(index, e)} >
<option value="">---Select---</option>
<option value="1">Single Sharing</option>
<option value="2">Two Bed Sharing</option>
<option value="3">Three Bed Sharing</option>
<option value="4">Four Bed Sharing</option>
<option value="5">Five Bed Sharing</option>
</select>
<label> No of Rooms:</label>
<input type="number" style={{ width: '60px' }} name="no_of_rooms" value={element.no_of_rooms || ""} onChange={e => handleChange(index, e)}></input>
<label> Rent per month:</label>
<input type="number" style={{ width: '100px' }} name="rent_per_month" value={element.rent_per_month || ""} onChange={e => handleChange(index, e)}></input>
<label> Total Capacity:</label>
<input type="number" style={{ width: '80px' }} name="total_capacity" value={element.total_capacity} onChange={e => handleChange(index, e)}></input>
<label> Vacancy:</label>
<input type="number" style={{ width: '50px' }} name="vacancy" value={element.vacancy || ""} onChange={e => handleChange(index, e)}></input>
{
index ?
<button type="button" className="button-remove" onClick={() => removeFormFields(index)}><ImBin size={20}/></button>
: null
}
</div>
))}
<div className="button-section">
<button className="button" type="button" onClick={() => addFormFields()}>Add</button>
<button className="button" type="button" onClick={resetForm}>Reset</button>
<button className="button" type="submit" onClick={(e) => saveRoomInfo(e)}>Submit</button>
</div>
</form>
</div>
</>
)
}
export default RoomInfo
Reference of Dynamic Form
The issue is while I am fetching the data from the form. But I can't figure it out. Thank you.

I am trying to set react-select values instead of using state for each select element i am trying to get all those values inside an array

When I use HTML select it works but it does not work with react-select
Also, the react-select options are arrays that I imported that return an object
Example:
const Program = [
{
value: "MSC",
label: "MSC"
},
{
value: "MCA",
label: "MCA",
isdisabled: true // disable this option
},
{
value: "BSC",
label: "BSC"
},
{
value: "BCA",
label: "BCA",
isdisabled: true // disable this option
}
];
import Select from "react-select";
function Download() {
const requestSelected = e => {
e.preventDefault();
// Axios.post('http://localhost:5000/login', attend)
// .then(res => {
// alert(res.data.message)
// })
console.log(selectedValue);
};
// set value for default selection
const [selectedValue, setSelectedValue] = useState({
Program: "",
Class: "",
Division: "",
Course: "",
Datefrom: "",
Datetill: ""
});
const handleChange = e => {
const { name, value } = e.target;
setSelectedValue({
...selectedValue,
[name]: value
});
};
return (
<form className="D Download" method="POST">
<p>Download Form:</p>
<label className="D_programlabel">Select Program:</label>
<Select
name="Program"
options={Program}
onChange={handleChange}
value={selectedValue.Program}
/>
<label className="D_classlabel">Select Class:</label>
<Select
name="Class"
options={Class}
onChange={handleChange}
value={selectedValue.Class}
/>
<label className="D_divisionlabel">Select Division:</label>
<Select
name="Division"
options={Division}
onChange={handleChange}
value={selectedValue.class}
/>
<label className="D_courselabel">Select Course:</label>
<Select
name="Course"
options={MSCCourses}
onChange={handleChange}
value={selectedValue.Course}
/>
<ul className="date_time-container">
<li>
<label>From Date :</label>
<input
type="date"
name="Datefrom"
placeholder="Enter Date"
onChange={handleChange}
value={selectedValue.Datefrom}
/>
</li>
<li>
<label>Till Date :</label>
<input
type="date"
name="Datetill"
placeholder="Enter Time"
onChange={handleChange}
value={selectedValue.Datetill}
/>
</li>
</ul>
<br />
<ul className="browse_upload-container">
<li>
<label>File :</label>
<input type="file" className="upload" accept=".csv" required></input>
<button type="submit" id="submit-file" class="btn btn-primary">
Download File
</button>
</li>
<li>
<input type="submit" value="Submit" onClick={requestSelected} />
</li>
</ul>
</form>
);
}
export default Download;
onChange return an item of options, so you should add name when call handleChange:
const handleChange = (name, value) => {
setSelectedValue({
...selectedValue,
[name]: value
})
}
onChange={(value) => handleChange("Program", value)}

Dynamically adding input elements to form in react

I am trying to dynamically add state to an array from my form in such a way that multiple inputs can eventually be saved as state in the same array, much like the redux form but I don't know Redux yet.
I don't know how to access the current array I am in so I can set the current input value as state. In this case, I am trying to save the name of the ingredient inside the ingredient array within the recipe state by mapping over the current array. Problem is when I try to map over the current array it apparently isn't an array at all and I get the following error when I try to enter data into the input..
TypeError: currentIngredients.map is not a function
(anonymous function)
src/components/recipes/Recipe_Form.js:68
65 | <input
66 | onChange={e => {
67 | const name = e.target.value;
> 68 | setRecipe((currentIngredients) =>
| ^ 69 | currentIngredients.map(x =>
70 | x.id === i.id
71 | ? {
. I am unsure how to access the ingredient array in such a way that I can edit state.
thanks
export const Recipe_Form = () => {
const recipeContext = useContext(RecipeContext);
const [recipe, setRecipe] = useState({
title: "",
img: "",
ingredients: [{ id: "1", name: "cheese", amount: "200" }],
method: "",
serves: "",
time: "",
});
const onChange = (e) =>
setRecipe({ ...recipe, [e.target.name]: e.target.value });
const onSubmit = (e) => {
e.preventDefault();
recipeContext.addRecipe(recipe);
setRecipe({
title: "",
img: "",
ingredients: [],
method: "",
serves: "",
time: "",
});
};
const { title, img, ingredients, method, serves, time } = recipe;
return (
<div className='row center-align'>
<form className='col s12' onSubmit={onSubmit}>
{/* Image */}
<div className='input-field col s12 center-align'>
<input
type='text'
placeholder='Please enter an image url'
name='img'
value={img}
onChange={onChange}
/>
</div>
{/* Title */}
<div className='input-field col s12 center-align'>
<input
type='text'
placeholder='Please enter recipe title'
className='validate'
name='title'
value={title}
onChange={onChange}
/>
</div>
{ingredients.map((i) => {
return (
<div key={i.id}>
<input
onChange={(e) => {
const name = e.target.value;
setRecipe((currentIngredients) =>
currentIngredients.map((x) =>
x.id === i.id
? {
...x,
name,
}
: x
)
);
// e.target.value
}}
value={i.name}
placeholder='ingredient'
/>
<input value={i.amount} placeholder='amount' />
</div>
);
})}
{/* Method */}
<div className='methodContainer'>
<div className='row method-line'>
<div className='input-field col s12 center-align'>
<input
type='text'
placeholder='Please enter method'
className='validate'
name='method'
value={method}
onChange={onChange}
/>
</div>
</div>
</div>
<a className='btn-floating btn-large waves-effect waves-light red'>
<i className='fas fa-plus'></i>
</a>
{/* Serves */}
<div className='input-field col s12 center-align'>
<input
type='number'
placeholder='Number of servings'
className='validate'
name='serves'
value={serves}
onChange={onChange}
/>
</div>
{/* Time */}
<div className='input-field col s12 center-align'>
<input
type='text'
placeholder='Time to compelete...'
className='validate'
name='time'
value={time}
onChange={onChange}
/>
</div>
<br></br>
<button
className='btn waves-effect waves-light'
type='submit'
name='action'>
Submit
</button>
</form>
</div>
);
};
export default Recipe_Form;
Isn't this, because the currentIngredients is actually your state object? you don't have to use it that way. You can do something like that:
setRecipe({
...recipe,
ingredients: recipe.ingredients.map(x =>
x.id === i.id
? {
...x,
name
}
: x
)
});

React setState object key with conditional event target

I have a problem on setting object keys in state. So below code is a form with title, body, city and country. With the value given by user, I set them to state. But since country and city should be assign in 'address' object, I created a handler that checks if the value is country/city or not.
But turned out even if the condition is not met, and ELSE is running, still it uses the object assign in if condition.
So either it is a title or city, state is set inside the address object
Would you check where am I doing wrong?
Below is my form and handlers
class Postit extends Component {
state = {
title: "",
body: "",
address: {
country: "",
city: ""
}
};
handleChange = e => {
if (e.target.id === "country" || "city") {
let address = Object.assign({}, this.state.address);
address[e.target.id] = e.target.value;
this.setState({ address });
console.log("NewState", this.state);
} else {
console.log("Target is not city or country");
this.setState({
[e.target.id]: e.target.value
});
}
};
handleSubmit = e => {
e.preventDefault();
console.log(this.state);
};
render() {
return (
<div className="container">
<form className="white" onSubmit={this.handleSubmit}>
<h5 className="grey-text text-darken-3">Send your post</h5>
{/* Title */}
<div className="input-field">
<input type="text" id="title" onChange={this.handleChange} />
<label htmlFor="title"> Title</label>
</div>
{/* Body */}
<div className="input-field">
<textarea
id="body"
className="materialize-textarea"
onChange={this.handleChange}
/>
<label htmlFor="body"> Content</label>
</div>
{/* City / Country Select */}
<div className="input-field">
<input type="text" id="country" onChange={this.handleChange} />
<label htmlFor="country"> Write your Country</label>
</div>
<div className="input-field">
<input type="text" id="city" onChange={this.handleChange} />
<label htmlFor="city"> Write your City</label>
</div>
<div className="input-field">
<button
className="btn pink lighten-1 center-align"
style={{ marginTop: "10px", borderRadius: "6px", width: "100%" }}
>
Post it
</button>
</div>
</form>
</div>
);
}
}
Exmp, when I fill the title, The newState looks like this:
{title: "", body: "", address: {country:"", city:"", title:"test"}}
Thank you!
You're not checking the condition properly. if (e.target.id === "country" || "city") is always going to be true even if e.target.id is not "country", due to the or part where "city" is a truthy value. It should be if (e.target.id === "country" || e.target.id === "city")
handleChange = e => {
if (e.target.id === "country" || e.target.id === "city") {
let address = Object.assign({}, this.state.address);
address[e.target.id] = e.target.value;
this.setState({ address });
console.log("NewState", this.state);
} else {
console.log("Target is not city or country");
this.setState({
[e.target.id]: e.target.value
});
}
};
Hope this helps !

Resources