Home > Back-end >  How to change <Image /> src on hover?
How to change <Image /> src on hover?

Time:11-23

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);
  // ...
}
  • Related