I am working on a form that takes an order from customers and I am using React Redux to manage my state. There is an array of object named items where I add the items of the order to it. Every item is an object of 4 properties (name, quantity, price, instructions). And here is how I handle my form :
for (let i = 0; i < numOfItems; i++) {
rows.push(
<div key={i} className="add-items-form">
<div className="items-header-container">
<h3 className="items-form-header">ITEM #{i + 1}</h3>
<Button onClick={() => removeItem(i)}>Remove Item</Button>
</div>
<div className="row">
<FormGroup className="col-md-6">
<Label >Item Name</Label>
<Input type="text" name="name" onChange={setFormValue} value={form.items.name} />
</FormGroup>
<FormGroup className="col-md-6">
<Label >QTY</Label>
<Input type="number" name="quantity" onChange={setFormValue} value={form.items.quantity} />
</FormGroup>
</div>
<div className="row">
<FormGroup className="col-md-6">
<Label >Price</Label>
<Input type="number" name="price" onChange={setFormValue} value={form.items.price} />
</FormGroup>
<FormGroup className="col-md-6">
<Label >Special Instructions</Label>
<Input type="text" name="special_instructions" onChange={setFormValue} value={form.items.special_instructions} />
</FormGroup>
</div>
</div>
)
};
This is the redux action that takes the input :
export function setFormValue(field) {
console.log("FIELD", field.target.value)
return function (dispatch) {
dispatch({ type: types.SET_FORM_VALUE, payload: field })
};
}
This is my reducer function where I am handling the part of the items in the form:
if (action.payload.target.name === "items") {
let cloneditems = [...state.form.items];
cloneditems.push(action.payload.target.value);
return {
...state,
form: {
...state.form,
items: cloneditems
}
};
}
What I am planning to do is to add an item object and push it to the items array every time I add an item with its details in my form. Yet, it adds them outside the items array not inside it as an object. Can anyone help me with this part as it consumes a lot of time and I cannot make it? Here is a link to my project if you want to have a look :
https://codesandbox.io/s/hopeful-williamson-nvqx9?file=/src/redux/reducer.js
and here is an example of what I am planning to get :
form : {
user :[{
name: "",
quantity:"",
price:"",
instructions:""
}]
}
Moreover, this is my reducer initial state :
const initialState = {
form: {
pickup_address: {},
delivery_address: {},
items: [{
index: 0,
name: "",
quantity: 0,
price: 0,
special_instructions: ""
}]
},
numOfItems: 0,
};
Any help ?!
It would be great if post CodeSandbox link,
I just posted simple way to add objects to an array, Maybe helpful.
var myArray = ["Red", "Green", "Blue"];
myArray.push("Yellow");
var myObject = {id: 1, name: "Amoos", class: 10};
myArray.push(myObject);
console.log(myArray)
https://codesandbox.io/s/cocky-haze-5p346?file=/src/forms/itemForm.js
Try it now
itemDetails
for (let i = 0; i < numOfItems; i++) {
rows.push(
<ItemForm
index={i}
removeItem={removeItem}
setItemName={setItemName}
setQuantity={setQuantity}
setPrice={setPrice}
setInstructions={setInstructions}
itemName={name}
qty={quantity}
price={price}
special_instructions={special_instructions}
/>
// <div key={i} className="add-items-form">
// <div className="items-header-container">
// <h3 className="items-form-header">ITEM #{i + 1}</h3>
// <Button onClick={() => removeItem(i)}>Remove Item</Button>
// </div>
// <div className="row">
// <FormGroup className="col-md-6">
// <Label>Item Name</Label>
// <Input
// type="text"
// name="name"
// onChange={e => setItemName(e.target.value)}
// value={itemName}
// />
// </FormGroup>
// <FormGroup className="col-md-6">
// <Label>QTY</Label>
// <Input
// type="text"
// name="quantity"
// onChange={e => setQuantity(e.target.value)}
// value={qty}
// />
// </FormGroup>
// </div>
// <div className="row">
// <FormGroup className="col-md-6">
// <Label>Price</Label>
// <Input
// type="text"
// name="price"
// onChange={e => setPrice(e.target.value)}
// value={price}
// />
// </FormGroup>
// <FormGroup className="col-md-6">
// <Label>Special Instructions</Label>
// <Input
// type="text"
// name="special_instructions"
// onChange={e => setInstructions(e.target.value)}
// value={instructions}
// />
// </FormGroup>
// </div>
// </div>
);
}
itemForm.js
import React, { useState } from "react";
import { Button, FormGroup, Input, Label } from "reactstrap";
function ItemForm(props) {
console.log("dssd", props.price);
return (
<div key={props.index} className="add-items-form">
<div className="items-header-container">
<h3 className="items-form-header">ITEM #{props.index + 1}</h3>
<Button onClick={() => props.removeItem(props.index)}>
Remove Item
</Button>
</div>
<div className="row">
<FormGroup className="col-md-6">
<Label>Item Name</Label>
<Input
type="text"
name="name"
onChange={e => props.setItemName(e.target.value)}
value={props.name}
/>
</FormGroup>
<FormGroup className="col-md-6">
<Label>QTY</Label>
<Input
type="text"
name="quantity"
onChange={e => props.setQuantity(e.target.value)}
value={props.quantity}
/>
</FormGroup>
</div>
<div className="row">
<FormGroup className="col-md-6">
<Label>Price</Label>
<Input
type="text"
name="price"
onChange={e => props.setPrice(e.target.value)}
value={props.price}
/>
</FormGroup>
<FormGroup className="col-md-6">
<Label>Special Instructions</Label>
<Input
type="text"
name="special_instructions"
onChange={e => props.setInstructions(e.target.value)}
value={props.instructions}
/>
</FormGroup>
</div>
</div>
);
}
export default ItemForm;
Related
I'm trying to save information in a table, which also has a field of type varbinary(max) (an image) (SQL Server)
class AddDoctor extends Component {
state = {
file: null,
name: "",
phoneNumber: "",
email: "",
status: "Active",
specialty: "",
specialities: [],
};
componentDidMount() {
const URL = "http://localhost:55317/api/TSpecialties";
ApiService.get(URL)
.then((data) => this.setState({ specialities: data }))
.catch((err) => console.log(err));
}
imgClick = () => {
const file = document.querySelector("#id-file");
file.click();
};
handleChange = (e) => {
const state = this.state;
state[e.target.name] = e.target.value;
this.setState({
...state,
});
};
handleFileChange = (event) => {
this.setState({
file: URL.createObjectURL(event.target.files[0]),
});
};
handleSubmit = (e) => {
e.preventDefault();
const URL = "http://localhost:55317/api/TDoctors/";
const DATA = {
doctorName: this.state.name,
doctorProfileImg: this.state.file,
doctorPhoneNumber: this.state.phoneNumber,
doctorEmail: this.state.email,
doctorStatus: this.state.status,
doctorSpecialtyId: Number(this.state.specialty),
};
let formData = new FormData();
formData.append("doctorProfileImg", DATA.doctorProfileImg);
formData.append("doctorName", DATA.doctorName);
formData.append("doctorEmail", DATA.doctorEmail);
formData.append("doctorPhoneNumber", DATA.doctorPhoneNumber);
formData.append("doctorStatus", DATA.doctorStatus);
formData.append("doctorSpecialtyId", DATA.doctorSpecialtyId);
const options = {
method: "POST",
body: formData
};
fetch(URL, options)
.then(res => console.log(res))
.catch(err => console.log("ERR: " + err))
};
render() {
return (
<div>
<form className="row g-3" onSubmit={this.handleSubmit}>
<div className="col-md-6">
<label htmlFor="name" className="form-label">
Name
</label>
<input
type="text"
className="form-control"
id="name"
name="name"
onChange={this.handleChange}
placeholder="Input your name"
/>
</div>
<div className="col-md-6">
<label htmlFor="email" className="form-label">
Email
</label>
<input
type="text"
onChange={this.handleChange}
name="email"
className="form-control"
id="email"
/>
</div>
<div className="col-md-6">
<label className="mb-2" htmlFor="phoneNumber">
Phone Number
</label>
<div className="input-group mb-2 mr-sm-2">
<div className="input-group-prepend">
<div className="input-group-text">+51</div>
</div>
<input
type="text"
onChange={this.handleChange}
className="form-control"
id="phoneNumber"
name="phoneNumber"
placeholder="Phone Number"
/>
</div>
</div>
<div className="col-md-12">
<label htmlFor="specialty" className="form-label">
Specialty
</label>
{/* */}
<select
id="specialty"
name="specialty"
onChange={this.handleChange}
className="form-select"
>
<option defaultValue>Choose...</option>
{this.state.specialities.map((sp) => (
<option value={sp.specialtyId}>
{sp.specialtyName}
</option>
))}
</select>
{/* */}
</div>
<div className="col-12 my-5">
<button
type="submit"
className="btn btn-outline-success w-100"
>
Save
</button>
</div>
</form>
<div className="col mx-5" style={{ minWidth: "250px" }}>
<img
src={ this.state.file}
id="img-select"
onClick={this.imgClick}
className="img-fluid img-thumbnail"
alt="doctor-img"
/>
<input
type="file"
style={{ display: "none" }}
onChange={this.handleFileChange}
id="id-file"
/>
</div>
</div>
);
}
}
Controller POST:
// POST: api/TDoctors
[HttpPost]
public async Task<IActionResult> PostTDoctor([FromBody] TDoctor tDoctor)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.TDoctor.Add(tDoctor);
await _context.SaveChangesAsync();
return CreatedAtAction("GetTDoctor", new { id = tDoctor.DoctorId }, tDoctor);
}
response on console developer tools when submit form:
and...
I have searched a lot of information and I cannot find the solution. I tried placing the multipart/form-data and it doesn't work either. I hope you can help me please.
Saving binary data to a SQL table is not a good practice, please use Azure Blob storage or some other storage service to store the files and just save the path of the file in the database column of a table
I can't find the solution to this error in my code every time I try to type something in my input field. TypeError: Cannot read property 'name' of undefined
but I've done like the answer in the following post https://stackoverflow.com/a/57519847/16568705 still not work
Here is all code
MultiStepForm.js
import { useForm, useStep } from "react-hooks-helper";
import React from "react";
import { LaporanPolisi } from "./stepForm/LaporanPolisi";
const defaultData = {
laporanPolisi: {
nomorLp: "",
jenisKelamin: 0,
tanggalLp: "",
kerugian: 0,
uraianSingkat: "",
pasalDilanggar: [
{
undangUndang: "",
pasal: ""
}
]
},
pelapor: [],
saksi: [],
korban: [],
terlapor: [],
barangBukti: [],
tkp: {
kodeProvinsi: "",
kodeKabupaten: "",
kodeKecamatan: "",
kodeKelurahan: "",
kodeRT: "",
kodeRW: ""
}
}
const steps = [
{ id: "laporanPolisi" },
{ id: "pelapor" },
{ id: "saksi" },
{ id: "korban" },
{ id: "terlapor" },
{ id: "barangBukti" },
{ id: "tkp" },
{ id: "submit" }
]
export const MultiStepForm = () => {
const [formData, setForm] = useForm(defaultData);
const { step, navigation } = useStep({
steps,
initialStep: 0,
});
const props = { formData, setForm, navigation };
switch (step.id) {
case "laporanPolisi":
return <LaporanPolisi {...props} />;
case "pelapor":
return "Pelapor";
case "saksi":
return "Saksi";
case "korban":
return "Korban";
case "terlapor":
return "Terlapor";
case "barangBukti":
return "Barang Bukti";
case "tkp":
return "TKP";
case "submit":
return "Submit";
default:
return <LaporanPolisi {...props} />;
}
};
LaporanPolisi.js
import React from 'react';
export const LaporanPolisi = ({ formData, setForm, navigation }) => {
const {
laporanPolisi
} = formData;
const handleChange = e => {
const { name, value } = e.target;
console.log(name)
setForm(prevState => ({
...prevState,
laporanPolisi: {
...prevState.laporanPolisi,
[name]: value
}
}));
};
return (
<div className="card">
<div className="card-header">
<h4>Laporan Polisi</h4>
</div>
<div className="card-body">
<div className="row">
<div className="col-md-6">
<div className="form-group">
<label htmlFor="nomorLp">Nomor Laporan</label>
<input onChange={handleChange} id="nomorLp"
placeholder="Nomor Laporan" type="text" className="form-control" name="nomorLp" value={laporanPolisi.nomorLp} />
</div>
</div>
<div className="col-md-6">
<div className="form-group">
<label htmlFor="jenisKelamin">Jenis Kelamin</label>
<select onChange={handleChange} value={laporanPolisi.jenisKelamin} id="jenisKelamin" name="jenisKelamin" className="custom-select">
<option value={0}>Laki-Laki</option>
<option value={1}>Perempuan</option>
</select>
</div>
</div>
{/* <input placeholder="Tanggal Laporan" type="text" className="form-control" name="nomorLp" value={jenisKelamin}></input>
<input placeholder="Kerugian" type="text" className="form-control" name="nomorLp" value={nomorLp}></input>
<input placeholder="Uraian Singkat" type="text" className="form-control" name="nomorLp" value={nomorLp}></input>
<input placeholder="Nomor Laporan" type="text" className="form-control" name="nomorLp" value={nomorLp}></input> */}
</div>
</div>
<pre>
<code>
{JSON.stringify(formData.laporanPolisi)}
</code>
</pre>
</div>
)
}
error Image:
https://imgur.com/a/JC89ykW
thanks before sorry for my bad english.
All react components have to return a component, for example: ...
In your MultiStepForm.js there are only 2 cases meet this requirement.
And this is the way I dealt with input, if you want to update setForm, you can add it in hanldeChange:
export default function BasicExample({ formData, setForm, navigation }) {
const [input1, setInput1] =useState('');
const [input2, setInput2] =useState('');
const handleChange = e => {
e.preventDefault()
console.log(input1)
console.log(input2)
};
return (
<form onSubmit={handleChange}>
<div className="col-md-6">
<div className="form-group">
<label htmlFor="nomorLp">Nomor Laporan</label>
<input onChange={e => setInput1(e.target.value)} id="nomorLp"
placeholder="Nomor Laporan" type="text" className="form-control" name="nomorLp" value={input1} />
</div>
</div>
<div className="col-md-6">
<div className="form-group">
<label htmlFor="jenisKelamin">Jenis Kelamin</label>
<select onChange={e => setInput2(e.target.value)} value={input2} id="jenisKelamin" name="jenisKelamin" className="custom-select">
<option value={0}>Laki-Laki</option>
<option value={1}>Perempuan</option>
</select>
</div>
</div>
<button type="submit"> Summit</button>
</form>
);
}
Resolved
i need to define name like this
https://imgur.com/POGiQ5z
in section Nested Object at https://github.com/revelcw/react-hooks-helper
and then, this is my fullcode after change:
LaporanPolisi.js
import React from 'react';
export const LaporanPolisi = ({ formData, setForm, navigation }) => {
const {
laporanPolisi
} = formData;
const handleSubmit = e => {
e.preventDefault();
};
return (
<div className="card">
<div className="card-header">
<h4>Laporan Polisi</h4>
</div>
<div className="card-body">
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col-md-6">
<div className="form-group">
<label htmlFor="nomorLp">Nomor Laporan</label>
<input onChange={setForm} id="nomorLp"
placeholder="Nomor Laporan" type="text" className="form-control" name="laporanPolisi.nomorLp" value={laporanPolisi.nomorLp} />
</div>
</div>
<div className="col-md-6">
<div className="form-group">
<label htmlFor="jenisKelamin">Jenis Kelamin</label>
<select onChange={setForm} id="jenisKelamin" name="laporanPolisi.jenisKelamin" className="custom-select" value={laporanPolisi.jenisKelamin}>
<option value={0}>Laki-Laki</option>
<option value={1}>Perempuan</option>
</select>
</div>
</div>
<div className="col-md-6">
<button className="btn btn-primary" onClick={() => navigation.previous()} type="button">Previous</button>
</div>
<div className="col-md-6">
<button className="btn btn-primary" onClick={() => navigation.next()} type="button">Next</button>
</div>
{/* <input placeholder="Tanggal Laporan" type="text" className="form-control" name="nomorLp" value={jenisKelamin}></input>
<input placeholder="Kerugian" type="text" className="form-control" name="nomorLp" value={nomorLp}></input>
<input placeholder="Uraian Singkat" type="text" className="form-control" name="nomorLp" value={nomorLp}></input>
<input placeholder="Nomor Laporan" type="text" className="form-control" name="nomorLp" value={nomorLp}></input> */}
</div>
</form>
</div>
<pre>
<code>
{JSON.stringify(laporanPolisi)}
</code>
</pre>
</div>
)
}
thanks everyone for answering my question
I want to add an item to a react state dictionary. Every time i press to submit i got the value is undefined on the dictionary object and it is append to the dictonary with a null values in name and cost but id is working fine. I find the issue but i am unable to find the solution. TIA.
import React, { Component } from "react";
import ExpenseItem from "./ExpenseItem.js";
class ExpensesList extends Component {
state = {
expenses: [{id: '12345' ,name: 'Pizza', cost: '20'}],
};
handleChange=(event)=>{
this.setState({[event.target.name]:event.target.value});
}
handleSubmit = (event) => {
event.preventDefault();
this.setState({expenses: [this.state.expenses,...[{id: Math.random(),name:this.state.expenses.name, cost:this.state.expense.cost}]]});
}
render() {
return (
<div>
<ul className="list-group">
{this.state.expenses.map((expense) => (
<ExpenseItem
id={expense.id}
name={expense.name}
cost={expense.cost}
/>
))}
</ul>
<div className ="row mt-3">
<h2> Add Expenses </h2>
<form onSubmit={this.handleSubmit}>
<div className="row">
<div className="form-group">
<label for="name">Name</label>
<input
required="required"
type="text"
className="form-control"
id="name"
value = {this.state.expenses.name}
onChange = {this.handleChange}
></input>
</div>
<div className="form-group">
<label for="name">Cost</label>
<input
required="required"
type="text"
className="form-control"
id="cost"
value = {this.state.expenses.cost}
onChange = {this.handleChange}
></input>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary"> Add Expense</button>
</div>
</div>
</form>
</div>
</div>
);
}
}
export default ExpensesList;
The issues is that this.state.expenses is an array. So, this.state.expenses.name and this.state.expenses.cost are both undefined.
If you want to be able to add new expense objects to the expenses array in state, you need a way to manage the new inputs in state. So, your state should look something like this:
state = {
expenses: [{id: '12345' ,name: 'Pizza', cost: '20'}],
cost: "",
name: ""
};
When a user inputs a cost and name, this.state.cost and this.state.name should be set in state, and then when the user clicks 'submit', a new object can be added to the this.state.expenses array.
(Also, your handleChange needs to specify which properties of state it intends to udpate)
Your final solution should look something like this:
import React, { Component } from "react";
import ExpenseItem from "./ExpenseItem.js";
class ExpensesList extends Component {
state = {
expenses: [{ id: '12345', name: 'Pizza', cost: '20' }],
cost: "",
name: ""
};
handleChange = (event, name) => {
this.setState({ [name]: event.target.value });
};
handleSubmit = (event) => {
event.preventDefault();
let newExpense = {
id: Math.random(),
name: this.state.name,
cost: this.state.cost
}
this.setState(prevState => ({
expenses: [...prevState.expenses, newExpense], // add new expense to expenses array
cost: "", // reset this field
name: "" // reset this field
}));
}
render() {
return (
<div>
<ul className="list-group">
{this.state.expenses.map((expense) => (
<ExpenseItem
id={expense.id}
name={expense.name}
cost={expense.cost}
/>
))}
</ul>
<div className="row mt-3">
<h2> Add Expenses </h2>
<form onSubmit={this.handleSubmit}>
<div className="row">
<div className="form-group">
<label for="name">Name</label>
<input
required="required"
type="text"
className="form-control"
id="name"
value={this.state.name}
onChange={e => this.handleChange("name")}
></input>
</div>
<div className="form-group">
<label for="name">Cost</label>
<input
required="required"
type="text"
className="form-control"
id="cost"
value={this.state.cost}
onChange={e => this.handleChange("cost")}
></input>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary"> Add Expense</button>
</div>
</div>
</form>
</div>
</div>
);
}
}
export default ExpensesList;
I am facing this when I am trying to set form error object. Basically, I want to show the errors below each input field. In response, I am getting an array of objects how do I set to my error object?
Error - Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
import axios from "axios";
import React, { useState, useEffect, useCallback } from "react";
import { Link } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { register } from "../actions/userActions";
const Register = () => {
const [countriesList, setCountriesList] = useState("");
const [userRegistration, setUserRegistration] = useState({
firstName: "",
lastName: "",
email: "",
password: "",
fullAddress: "",
city: "",
zipCode: "",
country: "",
phone: "",
terms: true,
});
const [userRegistrationError, setUserRegistrationError] = useState({
firstNameError: "",
lastNameError: "",
emailError: "",
passwordError: "",
fullAddressError: "",
cityError: "",
zipCodeError: "",
countryError: "",
phoneError: "",
termsError: "",
});
const dispatch = useDispatch();
const userRegister = useSelector((state) => state.userRegister);
const { loading, errors, success } = userRegister;
useEffect(() => {
const countries = async () => {
try {
const { data } = await axios.get(
`https://restcountries.eu/rest/v2/all`
);
setCountriesList(data);
} catch (err) {
console.error(err);
}
};
countries();
}, []);
useEffect(() => {
const handleErrors = (errors) => {
errors.map((error) => {
if (error.param === "firstname") {
setUserRegistrationError({
...userRegistrationError,
firstNameError: error.msg,
});
}
if (error.param === "email") {
setUserRegistrationError({
...userRegistrationError,
emailError: error.msg,
});
}
return null;
});
};
if (errors) {
handleErrors(errors);
}
}, [errors, setUserRegistrationError]);
const handleChange = (e) => {
const name = e.target.name;
const value = e.target.value;
setUserRegistration({ ...userRegistration, [name]: value });
};
const handleChkChange = (e) => {
const checked = e.target.checked;
console.log(checked);
setUserRegistration({ ...userRegistration, terms: checked });
};
const handleSubmit = (e) => {
e.preventDefault();
try {
dispatch(register());
} catch (error) {
console.error(error);
}
};
return (
<div className="form_container">
<form action="" onSubmit={handleSubmit}>
<div className="row no-gutters">
<div className="col-6 pr-1">
<div className="form-group">
<div className="form-group">
<input
type="text"
name="firstName"
className="form-control"
placeholder="First Name*"
value={userRegistration.firstName}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.firstNameError &&
userRegistrationError.firstNameError}
</p>
</div>
</div>
</div>
<div className="col-6 pr-1">
<div className="form-group">
<input
type="text"
className="form-control"
name="lastName"
placeholder="Last Name*"
value={userRegistration.lastName}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.lastNameError &&
userRegistrationError.lastNameError}
</p>
</div>
</div>
</div>
<hr />
<div className="private box">
<div className="row no-gutters">
<div className="col-6 pr-1">
<div className="form-group">
<input
type="email"
className="form-control"
name="email"
id="email_2"
placeholder="Email*"
value={userRegistration.email}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.emailError &&
userRegistrationError.emailError}
</p>
</div>
</div>
<div className="col-6 pl-1">
<div className="form-group">
<input
type="password"
className="form-control"
name="password"
id="password_in_2"
placeholder="Password*"
value={userRegistration.password}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.passwordError &&
userRegistrationError.passwordError}
</p>
</div>
</div>
<div className="col-12">
<div className="form-group">
<input
type="text"
name="fullAddress"
className="form-control"
placeholder="Full Address*"
value={userRegistration.fullAddress}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.fullAddressError &&
userRegistrationError.fullAddressError}
</p>
</div>
</div>
</div>
{/* /row */}
<div className="row no-gutters">
<div className="col-6 pr-1">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="City*"
name="city"
value={userRegistration.city}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.cityError &&
userRegistrationError.cityError}
</p>
</div>
</div>
<div className="col-6 pl-1">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="Postal Code*"
name="zipCode"
value={userRegistration.zipCode}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.zipCodeError &&
userRegistrationError.zipCodeError}
</p>
</div>
</div>
</div>
{/* /row */}
<div className="row no-gutters">
<div className="col-6 pr-1">
<div className="form-group">
<div className="custom-select-form">
<select
className="wide add_bottom_10 form-control"
name="country"
id="country"
value={userRegistration.country}
onChange={handleChange}
>
<option>Country*</option>
{countriesList &&
countriesList.map((country) => (
<option
key={country.alpha2Code}
value={country.alpha2Code}
>
{country.name}
</option>
))}
</select>
<p className="form-vald-error">
{userRegistrationError.countryError &&
userRegistrationError.countryError}
</p>
</div>
</div>
</div>
<div className="col-6 pl-1">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="Telephone *"
name="phone"
value={userRegistration.phone}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.phoneError &&
userRegistrationError.phoneError}
</p>
</div>
</div>
</div>
{/* /row */}
</div>
<hr />
<div className="form-group">
<label className="container_check">
Accept <Link to="#0">Terms and conditions</Link>
<input
type="checkbox"
name="terms"
checked={userRegistration.terms}
onChange={handleChkChange}
/>
<span className="checkmark" />
<p className="form-vald-error">
{userRegistrationError.termsError &&
userRegistrationError.termsError}
</p>
</label>
</div>
<div className="text-center">
<input
type="submit"
defaultValue="Register"
className="btn_1 full-width"
/>
</div>
</form>
</div>
);
};
export default Register;
your effect depends on userRegistrationError which is an object, reference based. Each time useEffect runs,setUserRegistrationError
creates a new object reference, which leads to an infinite loop since references won't be the same as the previous one.
One approach to avoid this issue and keep the right references, is to pass a callback function to setUserRegistrationError instead than a value. This way userRegistrationError is no longer a dependency, it will be an argument to your function instead:
useEffect(() => {
const handleErrors = (errors) => {
errors.forEach((error) => {
if (error.param === "firstName") {
// here you pass a callback function instead, and userRegistrationError is no longer a dependency
// and returns the next state as expected
setUserRegistrationError(userRegistrationError => ({
...userRegistrationError,
firstNameError: error.msg,
}));
}
if (error.param === "email") {
setUserRegistrationError(userRegistrationError => ({
...userRegistrationError,
emailError: error.msg,
}));
}
});
};
if (errors) {
handleErrors(errors);
}
}, [errors, setUserRegistrationError]);
You have a problem with the second useEffect, the first time you update your state userRegistrationError, the component re-rendered and re-executed the useeffect because the dependency userRegistrationError has changed and the process gets repeated again because the state gets updated every render.
useEffect(() => {
const handleErrors = (errors) => {
errors.map((error) => {
if (error.param === "firstname") {
setUserRegistrationError({
...userRegistrationError,
firstNameError: error.msg,
});
}
if (error.param === "email") {
setUserRegistrationError({
...userRegistrationError,
emailError: error.msg,
});
}
return null;
});
};
if (errors) {
handleErrors(errors);
}
}, [errors, setUserRegistrationError ]); //replace userRegistrationError by setUserRegistrationError
I Am populating values of my input field from JSON data what am getting from back-end, now there is an edit button on UI by on click on that button I am enabling my input field but not able to type inside as I am setting some value
I want to write inside the input once I have made them editable.
const { register, handleSubmit, errors } = useForm();
const [disabled, setdisabled] = useState(false);
const [editBtn, seteditBtn] = useState(true);
<form onSubmit={handleSubmit(onSubmit)}>
{editBtn === true && (
<div align="right">
<button
className="btn white_color_btn"
type="button"
onClick={edit}
>
Edit
</button>
</div>
)}
{editBtn === false && (
<button className="btn white_color_btn" type="submit">
Save
</button>
)}
<div className="row">
<div className="form-group col-6 col-sm-6 col-md-6 col-lg-4 col-xl-4">
<input
type="text"
disable
id="firstName"
name="firstName"
value={dataItems.firstname}
disabled={disabled ? "" : "disabled"}
ref={register({ required: true })}
/>
{errors.firstname && (
<span className="text-danger">first name required</span>
)}
<br />
<label htmlFor="emp_designation">First name</label>
</div>
<div className="form-group col-6 col-sm-6 col-md-6 col-lg-4 col-xl-4">
<input
type="text"
disabled
id="lastname"
name="lastname"
value={dataItems.lastname}
disabled={disabled ? "" : "disabled"}
ref={register({ required: true })}
/>
{errors.lastname && (
<span className="text-danger">last name required</span>
)}
<br />
<label htmlFor="lastname">Lastname</label>
</div>
</div>
</form>
On click of edit
const edit = () => {
setdisabled(true);
};
Code sandbox
You need to make your input as a controlled component and write onChange handlers which will update the state. This will allow you to edit the input field values. Demo
const [disabled, setdisabled] = useState(false);
const [name, setName] = useState(empData.item.name) // setting default name
const [lastname, setLastname] = useState(empData.item.lastname) // default lastname
const edit = () => {
setdisabled(true);
};
return (<div className="container-fluid">
<div align="right">
<button className="btn" onClick={edit}>
Edit
</button>
</div>
<div className="row">
<div>
<input
type="text"
disable
id="item.value"
value={name}
onChange={(e) => {
setName(e.target.value)
}}
disabled={disabled ? "" : "disabled"}
/>
<br />
<label htmlFor="name">Name</label>
</div>
<div>
<input
type="text"
disabled
id={"lastname"}
value={lastname}
onChange={(e) => {
setLastname(e.target.value)
}}
disabled={disabled ? "" : "disabled"}
/>
<br />
<label htmlFor="lastname">Lastname</label>
</div>
</div>
</div>);
Your input is controlled by the value you are giving to it. ie: Its value is always for example empData.item.name.
And you are not providing a change handler to handle the change.
Try adding something like this:
function myChangeHandler(e){
setEditedValueSomeHow(e.target.value);
}
<input
// ...
onChange={myChangeHandler}
/>
Read more about uncontrolled components
PS: you should have had a warning message in your console like this one:
Edit:
You are using react-hook-form to manage your form but at the same time giving values to your inputs.
Please refer to this link to initialize your form values.
short story:
Remove value form your input.
Pass an object to useForm hook containing initial values.
const { register, handleSubmit, errors } = useForm({
defaultValues: {
firstName: "steve",
lastname: "smith"
}
});
Here is a working fork for your codesandbox
In order to make the input editable, you need to update a local state which controlls the input value. As suggested by you in the comments, you are using graphql to get the data, you can make use of useEffect to set the data in state and then on click of edit, update the localState
export default function App() {
const { register, handleSubmit, errors } = useForm();
const [disabled, setdisabled] = useState(true);
const [editBtn, seteditBtn] = useState(true);
const { loading, data } = useQuery("some qraphql query here"); // getting data from graphql
const [formData, setFormData] = useState({});
useEffect(() => {
setFormData(data);
}, [data]);
const edit = () => {
setdisabled(false);
seteditBtn(false);
};
const onSubmit = () => {
console.log(formData);
// submit data using formData state.
};
const handleChange = e => {
const name = e.target.name;
const value = e.target.value;
console.log(name, value);
setFormData(prev => ({ ...prev, [name]: value }));
};
return (
<div className="container-fluid">
<form onSubmit={handleSubmit(onSubmit)}>
{editBtn === true && (
<div align="right">
<button
className="btn white_color_btn"
type="button"
onClick={edit}
>
Edit
</button>
</div>
)}
{editBtn === false && (
<button className="btn white_color_btn" type="submit">
Save
</button>
)}
<div className="row">
<div className="form-group col-6 col-sm-6 col-md-6 col-lg-4 col-xl-4">
<input
type="text"
id="firstname"
name="firstname"
onChange={handleChange}
value={formData.firstname}
disabled={disabled}
ref={register({ required: true })}
/>
{errors.firstname && (
<span className="text-danger">first name required</span>
)}
<br />
<label htmlFor="emp_designation">First name</label>
</div>
<div className="form-group col-6 col-sm-6 col-md-6 col-lg-4 col-xl-4">
<input
type="text"
id="lastname"
name="lastname"
value={formData.lastname}
onChange={handleChange}
disabled={disabled}
ref={register({ required: true })}
/>
{errors.lastname && (
<span className="text-danger">last name required</span>
)}
<br />
<label htmlFor="lastname">Lastname</label>
</div>
</div>
</form>
</div>
);
}
Working mock demo