I am using React and useState to capture my HTML form’s values. That seems to be working just fine. I have set default values for my user state so when I submit to the Express API on the backend it should be able to use those values stored in user state. Problem is after typing in values into the form, the first time it submits, it uses the default values, the second time I submit it works. So the values typed into the form, don’t actually get used until the second submit. Here is my code:
import { useState, useEffect } from "react";
const USER_DEFAULT = {
fav_color: "",
dist: "",
origin: "",
min_age: "",
max_age: "",
};
function UserForm() {
const [user, setUser] = useState(USER_DEFAULT);
const [users, setUsers] = useState([]);
useEffect(() => {
async function fetchData() {
const response = await fetch(`http://localhost:1234/users/`)
.then((res) => res.json())
.then((data) => setUsers(data))
.catch(console.log);
return response;
}
fetchData();
}, []);
const handleChange = (event) => {
const newUser = { ...user };
newUser[event.target.name] = event.target.value;
setUser(newUser);
console.log(newUser);
};
const getUsers = (event) => {
event.preventDefault();
let fetchUrl = "http://localhost:1234/users?";
if (user.fav_color) fetchUrl += `fav_color=${user.fav_color}&`;
if (user.dist) fetchUrl += `dist=${user.dist}&`;
if (user.origin) fetchUrl += `origin=${user.origin}&`;
if (user.min_age) fetchUrl += `min_age=${parseInt(user.min_age)}&`;
if (user.max_age) fetchUrl += `max_age=${parseInt(user.max_age)}&`;
console.log(fetchUrl); // fetchUrl is getting set correctly and seems to be working fine
fetch(fetchUrl)
.then((response) => response.json())
// this set doesn't work with the api call until I submit the form a 2nd time
.then((data) => setUsers(data))
.catch(console.log);
};
return (
<div className="container text-center">
<div className="row">
<div className="col-3 pr-5">
<form onSubmit={getUsers}>
<h4 className="mb-4">Find Users!</h4>
<div className="form-group">
<label htmlFor="fav_color">Favorite Color:</label>
<input
id="fav_color"
name="fav_color"
type="text"
className="form-control"
value={user.fav_color}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="row">Distance:</label>
<input
id="dist"
name="dist"
type="number"
className="form-control"
value={user.dist}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="origin">Origin (Long, Lat):</label>
<input
id="origin"
name="origin"
type="text"
className="form-control"
value={user.origin}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="min_age">Minimum Age:</label>
<input
id="min_age"
name="min_age"
type="number"
className="form-control"
value={user.min_age}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="row">Maximum Age:</label>
<input
id="max_age"
name="max_age"
type="number"
className="form-control"
value={user.max_age}
onChange={handleChange}
/>
</div>
<div className="mt-4">
<button className="btn btn-success mr-2" type="submit">
<i className="bi bi-file-earmark-check"></i> Search
</button>
</div>
</form>
</div>
</div>
</div>
);
}
export default UserForm;
I know this has something to do with setState not setting the values immediately after submitting the form. The changes are queued up and won’t take place until after the next page render correct? I also have read that I need to use an useEffect hook to get this to work but I have no idea how. I’ve tried one at the top of the code but it only sets the initial default values of the users state. I know there are a ton of answers out there for this issue but none of them really made sense to me. I couldn’t see how to use an useEffect hook here to make the state values update before I submit the form, not afterwards. Thanks for any help you can give!
2
Answers
user
state. You should use the functional update form insteadFor example, you could create a single function to fetch users with or without query parameters
then use that in your component, both in the effect hook and the
getUsers
functionFinally, in order to see any changes to the
users
state, you should render it somehowTry to not use async/await and .then/.catch at the same time. When replicated this code, it seems to work for me. You can check here
https://codesandbox.io/s/jovial-rubin-k13772?file=/src/UserForm.js:0-3506