useState hook returned undefined
, empty array []
, and/or empty object {}
after page refresh but returned actual data when I resave the code.
The data needs to always be available on every refresh as the data is stored in localStorage
.
Here is the code
const Admin = () => {
const { adminId } = useParams();
const navigate = useNavigate();
const [userId, setUserId] = useState<string>("");
const [results, setResults] = useState(false);
const [studentEnglishResults, setStudentEnglishResults] = useState<{
[key: string]: string;
}>({});
const [englishResults, setEnglishResults] = useState<string[]>([]);
const adminUser = adminUsers.find(
(adminUser) => adminUser.adminId === adminId
);
const userFound = users.find((user) => user.userId === userId);
useEffect(() => {
if (adminUser?.adminId !== adminId) {
navigate("/");
return
}
}, []);
useEffect(() => {
if (localStorage.length > 0) {
setUserId(JSON.parse(localStorage.getItem("userId") as string));
setResults(true);
setStudentEnglishResults(
JSON.parse(localStorage.getItem("englishAnswers") as string)
);
setEnglishResults(Object.keys(studentEnglishResults));
} else {
setResults(false);
}
console.log(userId); // This becomes undefined on every page refresh
console.log(studentEnglishResults); // This becomes empty {}
console.log(englishResults); // This becomes empty []
}, []);
useEffect(() => {
if (userFound) {
console.log(userFound); //This shows on code re-save
} else {
console.log("user not found"); //This shows on page refresh
}
}, []);
const generateResult = () => {};
const clearResult = () => {
localStorage.clear();
};
const backToLogin = () => {
navigate("/");
};
return (
<>
<div className="bg-gray-400 mx-auto w-3/4 p-3 mt-5">
<p className="capitalize font-serif text-center">
<span className="font-bold uppercase text-white text-2xl">
student result checker
</span>
</p>
</div>
<div className="my-10 flex justify-center gap-3 text-white">
<button
type="button"
className="btn bg-green-500"
onClick={generateResult}
>
Generate Results
</button>
<button type="button" className="btn bg-blue-500" onClick={backToLogin}>
Login
</button>
</div>
{results ? (
<div>
<div className="shadow-md shadow-gray-500 w-2/4 mx-auto p-5">
<p className="text-2xl text-gray-500">Answers selected by: </p>
<table className="table-auto border-collapse border w-full">
<thead>
<tr>
<th className="text-left">Question</th>
<th className="text-left">Answer</th>
</tr>
</thead>
<tbody>
{englishResults.map((item, index) => {
return (
<tr key={index}>
<td className="">{item}</td>
<td>{studentEnglishResults[item]}</td>
</tr>
);
})}
</tbody>
</table>
</div>
<div className="w-2/4 mx-auto mt-50 p-5">
<button
type="button"
className="btn bg-red-600 text-white"
onClick={clearResult}
>
Clear Results
</button>
</div>
</div>
) : (
<div className="w-2/4 bg-gray-400 p-5 mx-auto rounded-md">
<p className="text-4xl text-white text-center">
There is no result to display
</p>
</div>
)}
</>
);
};
export default Admin;
How do I retain the data after refresh??
CodePudding user response:
There is no need for a useEffect in this case, just use useState lazy initializer :
const [userId, setUserId] = useState(() => {
const uid = localStorage.getItem("userId");
return uid && JSON.parse(uid);
});
// ...
CodePudding user response:
I think you need to move the code that sets the state variables from localStorage
into a separate useEffect
hook that listens to changes in localStorage.
an example to this is to to pass a dependency array
to the useEffect hook that includes localstorage like this:
useEffect(() => {
if (localStorage.length > 0) {
setUserId(JSON.parse(localStorage.getItem("userId") as string));
setResults(true);
setStudentEnglishResults(
JSON.parse(localStorage.getItem("englishAnswers") as string)
);
setEnglishResults(Object.keys(studentEnglishResults));
} else {
setResults(false);
}
}, [localStorage]);
By the way, you need to ensure that you are setting the value of localstorage when you update the state.
useEffect(() => {
localStorage.setItem("userId", JSON.stringify(userId));
localStorage.setItem("englishAnswers", JSON.stringify(studentEnglishResults));
}, [userId, studentEnglishResults]);
Edit : As a good practice, use useEffect with a cleanup function to avoid memory leak when using 'navigate' inside the hook
useEffect(() => {
if (adminUser?.adminId !== adminId) {
const cleanup = () => {
navigate("/");
};
return cleanup;
}
}, [adminUser]);