Next.js 14.1.3
React 18.2.0
TailwindCSS 3.4.1
TailwindCSS-Merge 2.3.0
Node.js 20.16.0
Chrome 126.0.6478.185
Here is a video showing the differences in behavior in PC and mobile device emulators:
https://imgur.com/a/We924KI
On a PC, you can click again where it turned green and it will immediately turn white (actually, you have to remove the mouseover, though…).
On the mobile emulator, however, it remained green after another click and did not turn white until I clicked outside the element. How can this be resolved?
"use client";
import { useState, useEffect } from "react";
import { cn } from "@/app/lib/utils";
const gray_300 = "#d1d5db";
const emerald_300 = "#6ee7b7";
type SeatStatusType = "disabled" | "available" | "selected";
export default function Page() {
// prettier-ignore
const [availableSeats, setAvailableSeats] = useState<string[]>([
"A1",
]);
const [selectedSeats, setSelectedSeats] = useState<string[]>([]);
const addSelectedSeat = (v: string) => {
setSelectedSeats([...selectedSeats, v]);
};
const removeSelectedSeat = (v: string) => {
setSelectedSeats(selectedSeats.filter((seat) => seat !== v));
};
return (
<_TableGrid
_id="A"
addSelectedSeat={addSelectedSeat}
removeSelectedSeat={removeSelectedSeat}
availableSeats={availableSeats}
selectedSeats={selectedSeats}
/>
);
}
function _TableGrid({
_id,
addSelectedSeat,
removeSelectedSeat,
availableSeats,
selectedSeats,
}: {
_id: string;
addSelectedSeat: (seat: string) => void;
removeSelectedSeat: (seat: string) => void;
availableSeats: Readonly<string[]>;
selectedSeats: Readonly<string[]>;
}) {
const [status1, setStatus1] = useState<SeatStatusType>("available");
useEffect(() => {
for (const seatNum of ["1"]) {
const seat = _id + seatNum;
if (!availableSeats.includes(seat)) {
if (seatNum === "1") setStatus1("disabled");
continue;
} else if (selectedSeats.includes(seat)) {
if (seatNum === "1") setStatus1("selected");
continue;
} else {
if (seatNum === "1") setStatus1("available");
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [availableSeats, selectedSeats]);
return (
<div className="col-span-1 row-span-1 h-[60px] w-[45px]">
<RightUpperTableGrid
onClick={() => {
if (status1 === "disabled") return;
if (status1 === "available") {
// to disable
addSelectedSeat(_id + "1");
} else {
// to enable
removeSelectedSeat(_id + "1");
}
setStatus1(status1 === "available" ? "selected" : "available");
}}
color={status1 === "disabled" ? gray_300 : status1 === "selected" ? emerald_300 : ""}
/>
</div>
);
}
function TableSVGTag({
children,
border,
onClick,
color,
}: {
children: React.ReactNode;
border: number;
onClick?: () => void;
color?: string;
}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={45 + border}
height={60 + border}
viewBox={cn(2, 2, 47, 62)}
fill={color || "none"}
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={cn(
"lucide lucide-grid-2x2 m-0 block p-0 hover:cursor-pointer",
color === gray_300 ? "hover:fill-gray-300" : "hover:fill-emerald-300"
)}
onClick={onClick}
>
{children}
</svg>
);
}
export function RightUpperTableGrid({ onClick, color }: { onClick: () => void; color: string }) {
const width = 45;
const height = 60;
const radius = 5;
return (
<TableSVGTag onClick={onClick} color={color} border={2}>
<path
d={`M 3 3
L ${width + 3 - radius} 3
A ${radius} ${radius} 0 0 1 ${width + 3} ${3 + radius}
L ${width + 3} ${height + 3}
L 3 ${height + 3}
Z`}
/>
</TableSVGTag>
);
}
I want the element to be white on mobile without having to click outside of the element.
I am sure this is due to the fact that for some reason on mobile it is not re-rendering after a State update, but I don’t know how to force it.