Two-way data binding on large forms - reactjs

(After working through a React.js tutorial, I'm currently coding my first little app to get more practice. So this will be a newbie question and the answer is certainly out there somewhere, but apparently I don't know what to search for.)
Google lists a lot of examples on how to achieve two-way data binding for one input field. But what about large, complex forms, possibly with the option of adding more dynamically?
Let's say my form consists of horizontal lines of input fields. All lines are the same: First name, last name, date of birth and so on. At the bottom of the table, there is a button to insert a new such line. All this data is stored in an array. How do I bind each input field to its respective array element, so that the array gets updated when the user edits a value?
Working example with two lines of two columns each:
import { useState} from 'react';
function App() {
var [name1, setName1] = useState('Alice');
var [score1, setScore1] = useState('100');
var [name2, setName2] = useState('Bob');
var [score2, setScore2] = useState('200');
function changeNameHandler1 (e) {
console.log(e)
setName1(e.target.value)
}
function changeScoreHandler1 (e) {
setScore1(e.target.value)
}
function changeNameHandler2 (e) {
setName2(e.target.value)
}
function changeScoreHandler2 (e) {
setScore2(e.target.value)
}
return (
<div>
<table>
<tbody>
<tr>
<td><input name="name1" id="id1" type="text" value={name1} onChange={changeNameHandler1} /></td>
<td><input name="score1" type="text" value={score1} onChange={changeScoreHandler1} /></td>
</tr>
<tr>
<td><input name="name2" type="text" value={name2} onChange={changeNameHandler2} /></td>
<td><input name="score2" type="text" value={score2} onChange={changeScoreHandler2} /></td>
</tr>
</tbody>
</table>
{name1} has a score of {score1}<br />
{name2} has a score of {score2}<br />
</div>
);
}
export default App;
How do I scale this up without having to add handler functions for hundreds of fields individually?

You can still store your fields in an object and then just add to the object when you want to add a field. Then map through the keys to display them.
Simple example:
import { useState } from 'react'
const App = () => {
const [fields, setFields ] = useState({
field_0: ''
})
const handleChange = (e) => {
setFields({
...fields,
[e.target.name]: e.target.value
})
}
const addField = () => setFields({
...fields,
['field_' + Object.keys(fields).length]: ''
})
const removeField = (key) => {
delete fields[key]
setFields({...fields})
}
return (
<div>
{Object.keys(fields).map(key => (
<div>
<input onChange={handleChange} key={key} name={key} value={fields[key]} />
<button onClick={() => removeField(key)}>Remove Field</button>
</div>
))}
<button onClick={() => addField()}>Add Field</button>
<button onClick={() => console.log(fields)}>Log fields</button>
</div>
);
};
export default App;
Here is what I think you are trying to achieve in your question:
import { useState } from 'react'
const App = () => {
const [fieldIndex, setFieldIndex] = useState(1)
const [fields, setFields ] = useState({
group_0: {
name: '',
score: ''
}
})
const handleChange = (e, key) => {
setFields({
...fields,
[key]: {
...fields[key],
[e.target.name]: e.target.value
}
})
}
const addField = () => {
setFields({
...fields,
['group_' + fieldIndex]: {
name: '',
score: ''
}
})
setFieldIndex(i => i + 1)
}
const removeField = (key) => {
delete fields[key]
setFields({...fields})
}
return (
<div>
{Object.keys(fields).map((key, index) => (
<div key={key}>
<div>Group: {index}</div>
<label>Name:</label>
<input onChange={(e) => handleChange(e, key)} name='name' value={fields[key].name} />
<label>Score: </label>
<input onChange={(e) => handleChange(e, key)} name='score' value={fields[key].score} />
<button onClick={() => removeField(key)}>Remove Field Group</button>
</div>
))}
<button onClick={() => addField()}>Add Field</button>
<button onClick={() => console.log(fields)}>Log fields</button>
</div>
);
};
export default App;
You may want to keep the index for naming in which case you can use an array. Then you would just pass the index to do your input changing. Here is an example of using an array:
import { useState } from 'react'
const App = () => {
const [fields, setFields ] = useState([
{
name: '',
score: ''
}
])
const handleChange = (e, index) => {
fields[index][e.target.name] = e.target.value
setFields([...fields])
}
const addField = () => {
setFields([
...fields,
{
name: '',
score: ''
}
])
}
const removeField = (index) => {
fields.splice(index, 1)
setFields([...fields])
}
return (
<div>
{fields.map((field, index) => (
<div key={index}>
<div>Group: {index}</div>
<label>Name:</label>
<input onChange={(e) => handleChange(e, index)} name='name' value={field.name} />
<label>Score: </label>
<input onChange={(e) => handleChange(e, index)} name='score' value={field.score} />
<button onClick={() => removeField(index)}>Remove Field Group</button>
</div>
))}
<button onClick={() => addField()}>Add Field</button>
<button onClick={() => console.log(fields)}>Log fields</button>
</div>
);
};
export default App;

have multiple solutions to this problem for example:
Solution #1
Using useRef to store value of the field
import { useRef, useCallback } from "react";
export default function App() {
const fullNameInputElement = useRef();
const emailInputElement = useRef();
const passwordInputElement = useRef();
const passwordConfirmationInputElement = useRef();
const formHandler = useCallback(
() => (event) => {
event.preventDefault();
const data = {
fullName: fullNameInputElement.current?.value,
email: emailInputElement.current?.value,
password: passwordInputElement.current?.value,
passwordConfirmation: passwordConfirmationInputElement.current?.value
};
console.log(data);
},
[]
);
return (
<form onSubmit={formHandler()}>
<label htmlFor="full_name">Full name</label>
<input
ref={fullNameInputElement}
id="full_name"
placeholder="Full name"
type="text"
/>
<label htmlFor="email">Email</label>
<input
ref={emailInputElement}
id="email"
placeholder="Email"
type="email"
/>
<label htmlFor="password">Password</label>
<input
ref={passwordInputElement}
id="password"
placeholder="Password"
type="password"
/>
<label htmlFor="password_confirmation">Password Confirmation</label>
<input
ref={passwordConfirmationInputElement}
id="password_confirmation"
placeholder="Password Confirmation"
type="password"
/>
<button type="submit">Submit</button>
</form>
);
}
or still use useState but store all values in one object
import { useState, useCallback } from "react";
const initialUserData = {
fullName: "",
email: "",
password: "",
passwordConfirmation: ""
};
export default function App() {
const [userData, setUserData] = useState(initialUserData);
const updateUserDataHandler = useCallback(
(type) => (event) => {
setUserData({ ...userData, [type]: event.target.value });
},
[userData]
);
const formHandler = useCallback(
() => (event) => {
event.preventDefault();
console.log(userData);
},
[userData]
);
return (
<form onSubmit={formHandler()}>
<label htmlFor="full_name">Full name</label>
<input
id="full_name"
placeholder="Full name"
type="text"
value={userData.fullName}
onChange={updateUserDataHandler("fullName")}
/>
<label>Email</label>
<input
id="email"
placeholder="Email"
type="email"
value={userData.email}
onChange={updateUserDataHandler("email")}
/>
<label htmlFor="password">Password</label>
<input
id="password"
placeholder="Password"
type="password"
value={userData.password}
onChange={updateUserDataHandler("password")}
/>
<label htmlFor="password_confirmation">Password Confirmation</label>
<input
id="password_confirmation"
placeholder="Password Confirmation"
type="password"
value={userData.passwordConfirmation}
onChange={updateUserDataHandler("passwordConfirmation")}
/>
<button type="submit">Submit</button>
</form>
);
}
Solution #2
Or you have multiple libraries that also provide solutions like react-form-hook https://react-hook-form.com/

Related

React Datepicker - Uncaught RangeError: Invalid time value

Building a simple ToDo list app in ReactJS. Below is my Add Task functional component:
import React, { useState } from "react";
import TaskDataService from "../services/task.service";
import DatePicker from 'react-datepicker'
import FadeIn from 'react-fade-in';
import "react-datepicker/dist/react-datepicker.css";
import 'bootstrap/dist/css/bootstrap.min.css';
const AddTask = () => {
const initialTaskState = {
id: null,
title: "",
description: "",
completed: false,
startDate: new Date()
};
const [task, setTask] = useState(initialTaskState);
const [submitted, setSubmitted] = useState(false);
const handleInputChange = event => {
const { name, value } = event.target;
setTask({ ...task, [name]: value });
};
const saveTask = () => {
var data = {
title: task.title,
description: task.description,
startDate: task.startDate
};
TaskDataService.create(data)
.then(response => {
setTask({
id: response.data.id,
title: response.data.title,
description: response.data.description,
completed: response.data.completed,
startDate: response.data.startDate
});
setSubmitted(true);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
const newTask = () => {
setTask(initialTaskState);
setSubmitted(false);
};
return (
<FadeIn>
<div className="submit-form">
{submitted ? (
<div>
<h4>Task submitted successfully!</h4>
<button className="btn btn-success" onClick={newTask}>
Add
</button>
</div>
) : (
<div>
<div className="form-group">
<label htmlFor="title">Title</label>
<input
type="text"
className="form-control"
id="title"
required
value={task.title}
onChange={handleInputChange}
name="title"
/>
</div>
<div className="form-group">
<label htmlFor="description">Description</label>
<input
type="text"
className="form-control"
id="description"
required
value={task.description}
onChange={handleInputChange}
name="description"
/>
</div>
<div className="form-group">
<label htmlFor="startDate">Start Date</label>
<DatePicker
selected={ task.startDate }
onChange={date => handleInputChange({target: {value: date.toISOString().split("T")[0], name: 'startDate'}})}
name="startDate"
dateFormat="yyyy-MM-dd"
/>
</div>
<button onClick={saveTask} className="btn btn-success">
Submit
</button>
</div>
)}
</div>
</FadeIn>
);
}
export default AddTask;
When I try to select a date from the Datepicker calendar, app rendering crashes. There are a few errors but it looks like the main one is "Uncaught RangeError: Invalid time value". However, the task is successfully added to the database and I can view it upon reloading the app. But for whatever case, it crashes upon submit.
In contrast, this is my standalone Task component which also contains code for editing an existing task. In that component, everything works 100%. I can open the Datepicker calendar on the selected task, select a new date, and submit it succesfully with zero problems:
import React, { useState, useEffect } from "react";
import { useParams, useNavigate } from "react-router-dom";
import TaskDataService from "../services/task.service";
import DatePicker from 'react-datepicker';
import FadeIn from 'react-fade-in';
const Task = props => {
const { id } = useParams();
let navigate = useNavigate();
const initialTaskState = {
id: null,
title: "",
description: "",
completed: false,
startDate: new Date(),
};
const [currentTask, setCurrentTask] = useState(initialTaskState);
const [message, setMessage] = useState("");
const getTask = id => {
TaskDataService.get(id)
.then(response => {
setCurrentTask(response.data);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
useEffect(() => {
if (id)
getTask(id);
}, [id]);
const handleInputChange = event => {
const { name ,value } = event.target;
setCurrentTask({ ...currentTask, [name]: value });
};
const updateCompleted = status => {
var data = {
id: currentTask.id,
title: currentTask.title,
description: currentTask.description,
completed: currentTask.completed,
startDate: currentTask.startDate
};
TaskDataService.update(currentTask.id, data)
.then(response => {
setCurrentTask({ ...currentTask, completed: status });
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
const updateTask = () => {
TaskDataService.update(currentTask.id, currentTask)
.then(response => {
console.log(response.data);
setMessage("The task was updated successfully!");
})
.catch(e => {
console.log(e);
});
};
const deleteTask = () => {
TaskDataService.remove(currentTask.id)
.then(response => {
console.log(response.data);
navigate("/tasks");
})
.catch(e => {
console.log(e);
});
};
return (
<FadeIn>
<div>
{currentTask ? (
<div className="edit-form">
<h4>Task</h4>
<form>
<div className="form-group">
<label htmlFor="title">Title</label>
<input
type="text"
className="form-control"
id="title"
value={currentTask.title}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="description">Description</label>
<input
type="text"
className="form-control"
id="description"
value={currentTask.description}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label>
<strong>Status:</strong>
</label>
{currentTask.completed ? "Completed" : "Pending"}
</div>
<div className="form-group">
<label htmlFor="startDate">Start Date</label>
<DatePicker
onChange={date => handleInputChange({target: {value: date.toISOString().split("T")[0], name: 'startDate'}})}
name="startDate"
dateFormat="yyyy-MM-dd"
value={currentTask.startDate.toString().split("T")[0]}
/>
</div>
</form>
{currentTask.completed ? (
<button
className="badge badge-primary mr-2"
onClick={() => updateCompleted(false)}
>
Mark Pending
</button>
) : (
<button
className="badge badge-primary mr-2"
onClick={() => updateCompleted(true)}
>
Mark Complete
</button>
)}
<button
className="badge badge-danger mr-2"
onClick={deleteTask}
>
Delete
</button>
<button
type="submit"
className="badge badge-success"
onClick={updateTask}
>
Update
</button>
<p>{message}</p>
</div>
) : (
<div>
<br />
<p>Please click on a Task...</p>
</div>
)}
</div>
</FadeIn>
);
}
export default Task;
Done a lot of testing and research but no dice. Any ideas?
Commenter Konrad Linkowski provided the clue to the solution. I removed the line "startDate: response.data.startDate" and now it all works. I do not fully understand, so if anyone wants to explain the answer then please feel free.

update input of user in map function React hooks

New to react hooks...
I've brought some data of users from an API and I want to update one or more of the inputs by clicking a submit button.
I can see in the state that the user's last letter changes but I can see the new input on the screen and I can not change the entire word. And I don't know if it is supposed to change also in the whole array of users or it is impossible once I've called them from the API.
Thanks
import { useState } from 'react'
import axios from 'axios'
function UsersComp() {
const [user, setUser] = useState()
const [users, setUsers] = useState([])
const [id, setId] = useState(0)
const getUsers=async ()=>{
let resp = await axios.get("https://jsonplaceholder.typicode.com/users")
setUsers(resp.data);
}
const update = async (e) =>
{
e.preventDefault();
let resp = await axios.put("https://jsonplaceholder.typicode.com/users/" + id, user)
}
return (
<div calssname="App">
<form onSubmit={e => update(e)}>
{
users.map((item) =>
{
return <tbody key={item.id}>
<tr><td>
ID:{item.id} <br/>
Name:<input value={item.name} onChange={e => setUser({...user, name : e.target.value})} type="text" name="name" /> <br/>
Email:<input value={item.email} onChange={e => setUser({...user, email : e.target.value})} type="text" name="email" /> <br/>
<input type="button" value="Add Data"/>
<input type="submit" value="Update"/>
<input type="button" value="Delete"/><br/>
</td></tr>
</tbody>
})
}
</form>
<input type="button" value="Get users" onClick={getUsers} /> <br/>
</div>
);
}
export default UsersComp;
You can update onChange like this by using map:
onChange={(e) =>
setUser(
users.map((eachUser) =>
item.id === eachUser.id ? { ...eachUser, name: e.target.value } : eachUser,
),
)
}

The input value codes refused to work in react js

What could be wrong with these codes? The input is not working once I add [event.target.name]. If I remove that line of codes, I can see the contents that I type inside the input box. The issue is that I want it to work with this code [event.target.name]. This will enable me pick each inputbox values as entered by the user. There are three input boxes and I need to capture the three values in my useState. Any help on how to write it better?
import React, { useState } from 'react';
import "./addbirthday.css";
import "./home.css";
export default function Addbirthday({setShowAdd}) {
const [inputDatas, setInputData] = useState([
{fullName: '', fullDate: '', relationship: ''}
]);
const handlePublish = () =>{
console.log("Hi ", inputDatas)
}
const handleChangeInput = (index, event) =>{
const values = [...inputDatas];
values[index][event.target.name] = event.target.value
setInputData(values)
}
return (
<div className="container">
<div className= { closeInput? "addContainer" :"addWrapper homeWrapper "}>
<i className="fas fa-window-close" onClick={closeNow} ></i>
{inputDatas.map((inputData, index)=> (
<div key={index} className="addbirth">
<label>Name</label>
<input type="text" name="Fname" placeholder='Namend' value=
{inputData.fullName} onChange = {event => handleChangeInput(index, event)} />
<label>Date</label>
<input type="date" placeholder='Date' name="fdate" value=
{inputData.fullDate} onChange = {event => handleChangeInput(index, event)} />
<label>Relationship</label>
<input type="text" placeholder='Friend' name="frelationship" value=
{inputData.relationship} onChange = {event => handleChangeInput(index, event)}/>
</div>
))}
<button className="addBtn" onClick={handlePublish} >Add</button>
</div>
</div>
)
}
You are not setting the name correctly
Change your input tags name to same as state object name meaning
<input name='fullname' />
I have modified your code a bit. Make it as your own and get it done
Upvote my answer if it helps
https://codesandbox.io/s/jolly-khayyam-51ybe?file=/src/App.js:0-1711
import React, { useState } from "react";
export default function Addbirthday({ setShowAdd }) {
const [inputDatas, setInputData] = useState([
{ Fname: "", fdate: "", frelationship: "" }
]);
const handlePublish = () => {
console.log("Hi ", inputDatas);
};
const handleChangeInput = (index, event) => {
const values = [...inputDatas];
values[index][event.target.name] = event.target.value;
setInputData(values);
console.log(values[index][event.target.name]);
};
return (
<div className="container">
<div className={"addContainer addWrapper homeWrapper"}>
<i className="fas fa-window-close"></i>
{inputDatas.map((inputData, index) => (
<div key={index} className="addbirth">
<label>Name</label>
<input
type="text"
name="Fname"
placeholder="Namend"
value={inputData.fullName}
onChange={(event) => handleChangeInput(index, event)}
/>
<label>Date</label>
<input
type="date"
placeholder="Date"
name="fdate"
value={inputData.fullDate}
onChange={(event) => handleChangeInput(index, event)}
/>
<label>Relationship</label>
<input
type="text"
placeholder="Friend"
name="frelationship"
value={inputData.relationship}
onChange={(event) => handleChangeInput(index, event)}
/>
</div>
))}
<button className="addBtn" onClick={handlePublish}>
Add
</button>
</div>
</div>
);
}

Is it possible Preload a form with Firestore values?

I have a form that uploads its values to the Firestore database, and would like to use the same component for updating the values, so the question might really be - how to load initial state according to a conditional whether the props are passed?
The form
import Servis from "./funkc/servisni";
import React, { useState } from "react";
export default function ContactUpdate(props) {
//
console.log(props.item);
//
const initialState = {
id: props.item.id,
ime: props.item.Ime,
prezime: props.item.Prezime,
imeError: "",
prezimeError: "",
date: props.item.Datum,
kontakt: props.item.Kontakt,
kontaktError: "",
published: true,
};
const [theItem, setTheItem] = useState(initialState);
const [imeError, setImeError] = useState();
const [prezimeError, setPrezimeError] = useState();
const [message, setMessage] = useState();
const { item } = props;
if (theItem.id !== item.id) {
setTheItem(item);
}
const handleInputChange = (event) => {
const { name, value } = event.target;
setTheItem({ ...theItem, [name]: value });
};
const updatePublished = (status) => {
Servis.update(theItem.id0, { published: status })
.then(() => {
setTheItem({ ...theItem, published: status });
setMessage("The status was updated successfully!");
})
.catch((e) => {
console.log(e);
});
};
const updateItem = () => {
let data = {
Ime: theItem.ime,
Prezime: theItem.prezime,
Kontakt: theItem.kontakt,
Datum: theItem.date,
published: true,
};
Servis.update(theItem.id, data)
.then(() => {
setMessage("The tutorial was updated successfully!");
})
.catch((e) => {
console.log(e);
});
};
const deleteItem = () => {
Servis.remove(theItem.id)
.then(() => {
props.refreshList();
})
.catch((e) => {
console.log(e);
});
};
const validate = () => {
let imeError = "";
let kontaktError = "";
if (!theItem.ime) {
imeError = "obavezan unos imena!";
}
if (!theItem.kontakt) {
imeError = "obavezan unos kontakta!";
}
if (kontaktError || imeError) {
this.setState({ kontaktError, imeError });
return false;
}
return true;
};
return (
<div className="container">
{theItem ? (
<div className="edit-form">
<h4>Kontakt</h4>
<form>
<div className="form-group">
<label htmlFor="ime">Ime</label>
<input
type="text"
className="form-control"
// id="title"
name="ime"
value={theItem.Ime}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="Prezime">Prezime</label>
<input
type="text"
className="form-control"
// id="description"
name="Prezime"
value={theItem.Prezime}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="Datum">Datum</label>
<input
type="text"
className="form-control"
// id="description"
name="Datum"
value={theItem.Datum}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="Kontakt">Kontakt</label>
<input
type="text"
className="form-control"
// id="description"
name="Kontakt"
value={theItem.Kontakt}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label>
<strong>Status:</strong>
</label>
{theItem.published ? "Published" : "Pending"}
</div>
</form>
{theItem.published ? (
<button onClick={() => updatePublished(false)}>UnPublish</button>
) : (
<button onClick={() => updatePublished(true)}>Publish</button>
)}
<button onClick={deleteItem}>Delete</button>
<button type="submit" onClick={updateItem}>
Update
</button>
<p>{message}</p>
</div>
) : (
<div>
<br />
<p>Please click on a Tutorial...</p>
</div>
)}{" "}
</div>
);
}
The component passing the props:
import React, { useState, useEffect } from "react";
import firebase from "./firebase";
import { Link } from "react-router-dom";
export default function FireDetail({ match }) {
console.log(match);
console.log(match.params.id);
const [item, setItem] = useState([]);
const [loading, setLoading] = useState();
const getIt = () => {
setLoading(true);
const docRef = firebase
.firestore()
.collection("polja")
.doc(match.params.id)
.get()
.then((doc) => {
setItem(doc.data());
});
//
console.log(docRef);
//
// const docRef = firebase.firestore().collection("polja").doc("documentId")
//
setLoading(false);
};
useEffect(() => {
getIt();
}, [match]);
if (loading) {
return <h3>samo malo...</h3>;
}
return (
<div className="container">
<div>
{console.log("item: ", item)}
Kontakt: tip - email
<p> {item.Kontakt}</p>
</div>
<div>
<p>Datum rodjenja: {item.Datum}</p>
{item.Prezime} {item.Ime}
</div>
<Link to={`/kontakt/update/${item.id}`}> ajd </Link>
</div>
);
}
Or you might have an alternative idea on how to solve the problem?
indeed it is
export default function ContactUpdate(props) {
const initialState = {
ime: props.item.Ime,
prezime: props.item.Prezime,
datum: props.item.Datum,
kontakt: props.item.Kontakt,
id: props.Id,
};
const [theItem, setTheItem] = useState();
const [message, setMessage] = useState();
useEffect(() => {
setTheItem(props.item);
}, []);
const handleInputChange = (event) => {
const { name, value } = event.target;
setTheItem({ ...theItem, [name]: value });
console.log(theItem);
};
const updateItem = (theItem) => {
let data = {
Ime: theItem.Ime,
Prezime: theItem.Prezime,
Kontakt: theItem.Kontakt,
Datum: theItem.Datum,
};
console.log(updateItem());
Servis.update(theItem.id, data)
.then(() => {
setMessage("Uspjesno ste izmijenili unos!");
})
.catch((e) => {
console.log(e);
});
};
const deleteItem = () => {
Servis.remove(theItem.id).catch((e) => {
console.log(e);
});
};
return (
<div className="container">
{console.log(("theItem", props, theItem))}
{theItem ? (
<div className="edit-form">
<h4>Kontakt</h4>
<form>
<div className="form-group">
<label htmlFor="ime">Ime</label>
<input
type="text"
className="form-control"
name="Ime"
value={theItem.Ime}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="prezime">Prezime</label>
<input
type="text"
className="form-control"
name="Prezime"
value={theItem.Prezime}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="datum">Datum</label>
<input
type="text"
className="form-control"
name="Datum"
value={theItem.Datum}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="kontakt">Kontakt</label>
<input
type="text"
className="form-control"
name="Kontakt"
value={theItem.Kontakt}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label>
<strong>Status:</strong>
</label>
</div>
</form>
<button onClick={deleteItem}>Delete</button>
<button type="submit" onClick={updateItem}>
Update
</button>
<p>{message}</p>
</div>
) : (
<div>
<br />
<p>Odaberi jedan broj...</p>
</div>
)}{" "}
</div>
);
}

the state inside hooks are not updated for first time on form submit in react

I was trying to implement contactUS form in react using hooks.Contact us form is placed inside hooks.When I first submit the form the state in hooks are not updated ,when I click 2nd time states are set .and I am returning state to class component there api call are made.
//contactushook.js
import React, { useState } from 'react';
const ContactUshook = ({ parentCallBack }) => {
const [data, setData] = useState([]);
const handleSubmit = (event) => {
event.preventDefault();
setData({ name: document.getElementById('name').value, email: document.getElementById('email').value, message: document.getElementById('message').value });
console.log(data);
parentCallBack(data);
}
return <React.Fragment>
<div className="form-holder">
<form onSubmit={handleSubmit}>
<div>
<input id="name" type="text" placeholder="enter the name"></input>
</div>
<div>
<input id="email" type="email" placeholder="enter the email"></input>
</div>
<div>
<textarea id="message" placeholder="Type message here"></textarea>
</div>
<button type="submit" >Submit</button>
</form>
</div>
</React.Fragment >
}
export default ContactUshook;
//contactus.js
import React, { Component } from 'react';
import ContactUshook from './hooks/contactushook';
import '../contactUs/contactus.css';
class ContactComponent extends Component {
onSubmit = (data) => {
console.log('in onsubmit');
console.log(data);
}
render() {
return (
<div>
<h4>hook</h4>
<ContactUshook parentCallBack={this.onSubmit}></ContactUshook>
</div>
);
}
}
export default ContactComponent;
Stop using document queries and start using state instead!
Your ContactUshook component should look like this:
const ContactUshook = ({ parentCallBack }) => {
const [data, setData] = useState({ name: '', email: '', message: '' });
const handleSubmit = () => {
event.preventDefault();
parentCallBack(data);
}
const handleChange = (event, field) => {
const newData = { ...data };
newData[field] = event.target.value;
setData(newData);
}
return (
<div className="form-holder">
<form onSubmit={handleSubmit}>
<div>
<input
id="name"
type="text"
value={data.name}
placeholder="enter the name"
onChange={(e) => handleChange(e,'name')} />
</div>
<div>
<input
id="email"
type="email"
value={data.email}
placeholder="enter the email"
onChange={(e) => handleChange(e,'email')} />
</div>
<div>
<textarea
id="message"
value={data.message}
placeholder="Type message here"
onChange={(e) => handleChange(e,'message')} />
</div>
<button type="submit" >Submit</button>
</form>
</div>
);
}

Resources