I am creating a project where we can add and edit a user. For editing a user I am sending id as a url parameter using react-router-dom
Link
from one page to other and with the useParams
hook. I am fetching that id
. This id
I am using for setting the data to form. I have all users stored in redux store
. Now the question is when I reload the page the data is taking some time to load from redux store
and the setValue
of useFormHook
is throwing an error.
import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { useSelector } from "react-redux";
import { useParams } from "react-router-dom";
import { fetchData } from "./utils/index";
import { UpdateStudent} from "./utils/index";
function Update() {
const users = useSelector((state) => state.students);
console.log(users);
const [students, setStudents] = useState([]);
const {
register,
handleSubmit,
setValue,
formState: { errors, isDirty, isValid },
} = useForm({ mode: "onChange" });
let { id } = useParams();
var user = students && students.filter((u) => u.id === parseInt(id));
useEffect(() => {
fetchData();
}, []);
useEffect(() => {
setStudents(users);
}, [users]);
useEffect(() => {
setValue("name", user.name);
}, []);
const onUpdate = (data) => {
};
return (
<form onSubmit={handleSubmit(onUpdate)}>
<div>
<input
{...register("name", {
})}
type="text"
className="form-control"
placeholder="Name"
/>
</div>
<button
color="primary"
type="submit"
disabled={!isDirty || !isValid}
>
Save
</button>
</form>
);
}
export default Update;
CodePudding user response:
The way I solve this in my current project is that I split the component into 2, one component gets the user, one is only rendered when the user is available.
function Update() {
const users = useSelector((state) => state.students);
console.log(users);
const [students, setStudents] = useState([]);
let { id } = useParams();
var user = students && students.filter((u) => u.id === parseInt(id));
useEffect(() => {
fetchData();
}, []);
useEffect(() => {
setStudents(users);
}, [users]);
if(!user) return null; // or progress indicator
return <UpdateForm user={user} />
}
function UpdateForm({user})
const {
register,
handleSubmit,
setValue,
formState: { errors, isDirty, isValid },
} = useForm({ mode: "onChange" });
useEffect(() => {
setValue("name", user.name);
}, []);
const onUpdate = (data) => {
};
}, [user.name]);
return (
<form onSubmit={handleSubmit(onUpdate)}>
<div>
<input
{...register("name", {
})}
type="text"
className="form-control"
placeholder="Name"
/>
</div>
<button
color="primary"
type="submit"
disabled={!isDirty || !isValid}
>
Save
</button>
</form>
);
}
export default Update;
CodePudding user response:
Besides the issues I mentioned in comment above, the code doesn't handle when the students
state updates. You could simplify the code a bit and consume the students
state directly instead of duplicating it locally. Since the redux state is sometimes not available on the initial render cycle you'll need to use an useEffect
with a dependency on the state used to update form state.
function Update() {
const users = useSelector((state) => state.students);
const {
register,
handleSubmit,
setValue,
formState: { errors, isDirty, isValid },
} = useForm({ mode: "onChange" });
const { id } = useParams();
useEffect(() => {
fetchData();
}, []);
useEffect(() => {
const user = users?.filter((u) => u.id === Number(id));
if (user) {
setValue("name", user.name);
}
}, [users]); // <-- add users to dependency to update form state
...
if (/* no form state yet */) {
return "Loading..."; // or some loading indicator
}
return (
... // form UI
);
}