I am making an app the is supposed to flash a message indicating user creation success or failure. I am using deno's fresh framework which employs "preact". Preact is essentially a minimal version of react so just assume that what I'm doing would work the same in a React page.
My webpage code looks like this:
// import { useState } from "preact/hooks";
import { useState, useEffect } from "preact/hooks";
import { Handlers, PageProps } from "$fresh/server.ts";
import UserDb from "../database.ts";
interface CreatorProps {
email: string,
key: string
}
export default function Creator(props: CreatorProps) {
const [alertPointer, setAlertPointer] = useState(0);
const [alertInst, setAlertInst] = useState("");
useEffect(() => {
function createAlert() {
if (alertPointer === 0) {
null;
} else if (alertPointer === 1) {
setAlertInst(
<div role="alert">
<p >User Creation Succeeded</p>
<p >A new user was added to the database</p>
</div>
);
} else {
setAlertInst(
<div role="alert">
<strong >User Creation Failed</strong>
<span >Are you sure you entered in a valid email and key?</span>
<span >
<svg role="button" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><title>Close</title><path d="M14.348 14.849a1.2 1.2 0 0 1-1.697 0L10 11.819l-2.651 3.029a1.2 1.2 0 1 1-1.697-1.697l2.758-3.15-2.759-3.152a1.2 1.2 0 1 1 1.697-1.697L10 8.183l2.651-3.031a1.2 1.2 0 1 1 1.697 1.697l-2.758 3.152 2.758 3.15a1.2 1.2 0 0 1 0 1.698z"/></svg>
</span>
</div>
)
}
}
const timeId = setTimeout(() => {
// After 3 seconds set the show value to false
setAlertPointer(0);
setAlertInst("");
}, 3000)
function removeItem() {
console.log("rm item start");
clearTimeout(timeId)
console.log("rm item finished");
console.log(timeId);
console.log(alertPointer);
console.log(alertInst);
}
createAlert();
removeItem();
}, [alertPointer, alertInst]);
async function handleSubmit(event) {
event.preventDefault();
const emailInput = event.target.email;
const ageInput = event.target.key;
const resp = await createNewUser(emailInput.value, ageInput.value);
return resp
};
async function createNewUser(email, key) {
const rawPosts = await fetch('http://localhost:8000/api/createUser', {
"method": "POST",
"headers": {
"content-type": "application/json"
},
"body": JSON.stringify({
email: email,
key: key,
})
});
const respns = JSON.parse(await rawPosts.json());
if (respns.isUserCreated) {
setAlertPointer(1);
} else {
setAlertPointer(2);
}
}
return (
<div>
{alertInst}
<h1 > Search </h1>
<form method="post" onSubmit={async (e) => handleSubmit(e)}>
<input id="email" name="email" />
<input id="key" name="key" />
<br />
<button
type="submit">Submit
</button>
</form>
<br />
{/* <ul>
{results.map((name) => <li key={name}>{name}</li>)}
</ul> */}
</div>
);
};
When I create a user that succeeds the page looks like this:
But the message never disappears and the web console looks like this:
How do I fix this? How do I make the alert indicating success or failure vanish after 3 seconds?
CodePudding user response:
The issue I see is that once the alertPointer
state is updated to a non-zero value it's never set back to 0
. The removeItem
is called and clears the running timeout that was just instantiated to reset the alertPointer
state back to 0. alertPointer
equals 1
and state updates are continued to be enqueued.
useEffect(() => {
function createAlert() {
if (alertPointer === 0) {
null;
} else if (alertPointer === 1) {
setAlertInst(....);
} else {
setAlertInst(....)
}
}
const timeId = setTimeout(() => { // (1) <-- timeout instantiated
// After 3 seconds set the show value to false
setAlertPointer(0);
setAlertInst("");
}, 3000)
function removeItem() {
console.log("rm item start");
clearTimeout(timeId); // (3) <-- cleared
console.log("rm item finished");
console.log(timeId);
console.log(alertPointer);
console.log(alertInst);
}
createAlert();
removeItem(); // (2) <-- timeout cleared
}, [alertPointer, alertInst]);
You should store the timer id in a React ref (via the useRef
hook) and return useEffect
cleanup function in the case the component unmounts prior to the timeout expiring. This allows the normal operation of the timeout while the component is mounted.
Example:
const timerIdRef = useRef();
// Clear any running timeouts when the component unmounts
useEffect(() => {
return () => {
clearTimeout(timerIdRef.current);
};
}, []);
useEffect(() => {
function createAlert() {
if (alertPointer === 0) {
null;
} else if (alertPointer === 1) {
setAlertInst(....);
} else {
setAlertInst(....)
}
}
timerIdRef.current = setTimeout(() => {
// After 3 seconds set the show value to false
setAlertPointer(0);
setAlertInst("");
}, 3000)
createAlert();
}, [alertPointer, alertInst]);
CodePudding user response:
try to add dependencies in use effect