I have a Status
component that takes a log
prop and depending on log.status
, sets the Next.js <Image />
.
I want it so that the default log.status
triggers a mouseOver
and mouseOut
function to replace the Image src:
import Image from 'next/image';
import A from "../../public/A.svg";
import B from "../../public/B.svg";
import C from "../../public/C.svg";
import default from "../../public/default.svg";
function Page() {
const { data: log } = useSWR(URL, fetcher)
return(
<div>
<Status log={log} />
</div>
)
}
function Status({ log }) {
const [imgSrc, setImgSrc] = useState();
switch (log?.status) {
case "A":
setImgSrc(A);
break;
case "B":
setImgSrc(B);
break;
case "C":
setImgSrc(C);
break;
case "":
default:
setImgSrc(default);
break;
}
function mouseOverHandler(e) {
if (!log?.status) {
setImgSrc(defaultHover);
}
}
function mouseOutHandler(e) {
if (!log?.status) {
setImgSrc(default);
}
}
return (
<div
onMouseOver={mouseOverHandler}
onMouseOut={mouseOutHandler}
>
<Image src={imgSrc} />
</div>
);
}
This gives the following error:
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
When I change Status
to use a variable instead of a state:
function Status({ log }) {
let imgSrc;
switch (log?.status) {
case "A":
imgSrc = A;
break;
case "B":
imgSrc = B;
break;
case "C":
imgSrc = C;
break;
case "":
default:
imgSrc = default;
break;
}
function mouseOverHandler(e) {
if (!log?.status) {
imgSrc = defaultHover;
}
}
function mouseOutHandler(e) {
if (!log?.status) {
imgSrc = default;
}
}
return (
<div
onMouseOver={mouseOverHandler}
onMouseOut={mouseOutHandler}
>
<Image src={imgSrc} />
</div>
);
}
The page renders but hovering over Status
doesn't do anything.
I can get my code to work if I bypass <Image />
completely and use <img src={imgSrc} />
and e.currentTarget.firstElementChild.src="..."
in my mouseOverHandler
and mouseOutHandler
instead... but how or is it possible to get this to work with <Image />
?
CodePudding user response:
The problem is that you’re updating state unconditionally during render. State updates trigger a new render, in which you do a state update, which triggers another render, in which you do a state update…
The fix is to set the initial state when you call useState
instead of trying to update it immediately afterward.
You could use a mapping of statuses and do a lookup, for example:
const logImages = { A, B, C };
function Status({log}) {
const [imgSrc, setImgSrc] = useState(logImages[log?.status] || default);
// ...
}