Build a Grid Light Box in React (lights deactivate in reverse order of activation)
Track an ordered array of activated cell ids in state. On click, if not active, push; if already active, ignore (or toggle off). Once all activated, deactivate one-by-one (setInterval) in reverse order via .pop(). Render: each cell highlights if its id is in the array. Clean up the interval on unmount and when the array empties.
Classic React coding question — tests state, ordering, and timing.
Spec recap
- 3x3 grid (or NxN).
- Click an unlit cell → it lights up.
- Track activation order.
- Once all N cells are lit, deactivate them one-by-one (e.g. every 300ms) in reverse activation order.
- Clicking a lit cell during activation phase: typically ignored.
Implementation
import { useState, useEffect } from 'react';
const N = 9;
export function LightGrid() {
const [active, setActive] = useState<number[]>([]);
const [deactivating, setDeactivating] = useState(false);
function onClick(i: number) {
if (deactivating) return;
if (active.includes(i)) return;
setActive((prev) => [...prev, i]);
}
useEffect(() => {
if (active.length !== N) return;
setDeactivating(true);
const id = setInterval(() => {
setActive((prev) => {
const next = prev.slice(0, -1); // pop last
if (next.length === 0) {
clearInterval(id);
setDeactivating(false);
}
return next;
});
}, 300);
return () => clearInterval(id);
}, [active.length]);
return (
<div className="grid grid-cols-3 gap-2">
{Array.from({ length: N }, (_, i) => (
<button
key={i}
onClick={() => onClick(i)}
disabled={deactivating}
className={
'h-16 w-16 rounded border ' +
(active.includes(i) ? 'bg-green-500' : 'bg-gray-200')
}
/>
))}
</div>
);
}Walk-through
activeis the ordered list of activated cell indices.onClick: add if not present and not deactivating.- When
active.length === N, kick off deactivation interval. - Each tick pops from the end (reverse activation order).
- Clean up interval on unmount and when the array empties.
Edge cases / interview probes
- Double-click: ignored (
includesguard). - Unmount mid-deactivation: cleanup clears interval.
- Variable grid size: parametrize
N. - Animate the light fade: add CSS transition on the bg color;
transition-colors duration-200. - Pause on hover: clear interval on
onMouseEnter, restart on leave (extra credit). - What if user clicks before all 9 fill? Just adds to active.
Variations interviewers ask
- Reverse with delay reuse (use the time intervals between activations).
- Re-activatable after full cycle.
- N×M grid with different N.
- Animation per cell with transition end events.
Anti-patterns
- Tracking active as a
Set— loses order. - Setting state inside the effect's cleanup function.
- Using a single
useStatefor both array and flag (couples them).
Interview framing
"Array of activated cell ids in state — keeps order. onClick guards against double-add and against clicking during deactivation. Effect watches active.length; when it reaches N, kick off a setInterval that pops the last element each tick (reverse order). Cleanup clears the interval on unmount and when the array empties. Add a deactivating flag to disable clicks during the deactivation phase. The key is using an array (preserves order) not a Set, and putting deactivation in an effect so cleanup is automatic."
Follow-up questions
- •What if the user could click during deactivation?
- •How would you pause on hover?
- •How would you animate the light fading out?
Common mistakes
- •Using Set (loses order).
- •Missing cleanup on interval.
- •Allowing clicks during deactivation.
Performance considerations
- •Trivial. State + interval; one re-render per tick.
Edge cases
- •Unmount mid-deactivation.
- •Rapid clicks before fill.
- •Variable N.
Real-world examples
- •Frontend coding round at many companies (Atlassian, Razorpay, etc.).