My First Live Coding Experience: Potato Brain đ„
The first time you do anything, itâs always memorable. So, I decided to write a little something about my first live coding interview.
We started out with a very lightweight App.tsx fileâjust 43 lines in total. The instructions were straightforward:
âOptimize it.â
The code I was given looked like this:
import { type FC, type PropsWithChildren, useEffect, useState } from "react";
export default function App() { const [tick, setTick] = useState(0); const [item, setItem] = useState(0);
useEffect(() => { setInterval(() => setTick((t) => t + 1), 500); }, []);
6 collapsed lines
const items = [ { text: "Button 1" }, { text: "Button 2" }, { text: "Button 3" }, { text: "Button 4" }, ];
return ( <> <div>tick: {tick}</div> <div>item: {item}</div> {items.map((item, i) => ( <Button key="{i}">{item.text}</Button> ))} </> );}
6 collapsed lines
const Button: FC<PropsWithChildren<{ onClick?:> void }>> = ({ onClick, children,}) => { return <button onClick={onClick}>{children}</button>;};A Naive Mind at Work
The very first thing that caught my eye was a missing cleanup function.
I noticed the setInterval didnât have a cleanup function for when the component unmounts. Plus, in development mode with React Strict Mode enabled, it fires twice. So, I quickly added the cleanup:
useEffect(() => { const id = setInterval(() => setTick((t) => t + 1), 500); return () => clearInterval(id); }, []);Secretly thinking to myself:
âHey, I did this! Iâm getting hired right after this, right?â
Obviously, it probably had a positive effect on the interviewer, but itâs a little funny looking back at my naive mindset.
After doing that, and since I like to keep things simple, I noticed that I could solve the entire problem by extracting a component. One of the states, tick, was updating every 500 milliseconds. I realized that the rest of the function body and the JSX didnât even rely on tick and were needlessly re-rendering.
I was convinced I needed to extract the element rendering tick, along with its useState and useEffect, into its own isolated component (State Colocation). That way, it could re-render as much as it wanted without dragging the rest of the app down with it.
But it turned out the interviewer, very reasonably, just wanted to test my knowledge about React rendering mechanics instead of having me actually solve the problem with the simplest solution possible:
âHold on a second, donât get too attached to that solution. Imagine you arenât allowed to define those two states anywhere else. The goal is to optimize exactly what is rendering right here and prevent unnecessary re-renders.â
And that was the exact moment my brain just became a complete potato.
Hint #1: React.memo and the Mental Block (đ„)
The first hint they gave me was to use React.memo. I quickly wrapped the Button component, and boomâour re-rendering issue was solved! But the interviewer replied:
âThe interview isnât over yet. I want you to hook up
setItemso that when a button is clicked, ouritemstate becomes the index of that specific button.â
Because I was still internally stuck on my previous idea of component extraction and state colocation, I was just throwing out random theories. When it came to actually writing the code, my mind had completely locked up.
I went down to the Button component, added an onClick prop, and passed it an inline arrow function that called setItem with the buttonâs index. We also threw a console.log into the Button component to see what would happen.
{items.map((item, i) => ( <Button key={i} onClick={() => setItem(i)}>{item.text}</Button> ))} </> ); }
const _Button: FC<PropsWithChildren<{ onClick?: () => void }>> = ({ onClick, children, }) => { console.log("RENDERING BUTTON"); return <button onClick={onClick}>{children}</button>; };
const Button = React.memo(_Button);Surprise, surprise! Because the arrow function creates a brand-new function reference every single time the parent component (App) renders, React.memo looked at the new reference, assumed the prop had changed, and went ahead and re-rendered all the buttons anyway!
Hint #2: Caching and the Over-Engineering Phase
At this point, my brain had officially flatlined, and I couldnât come up with a proper solution. The interviewer dropped a hint about caching. So, I jumped in and defined a useCallback, which eventually led us to this monstrosity:
// ...const btnCallbacks = new Map();
export default function App() {// ... const changeItem = useCallback((index: number) => { const cb = btnCallbacks.get(index); if (cb) { return cb; } const newCb = () => setItem(index); btnCallbacks.set(index, newCb); return newCb; }, []);
return ( <>{/* ... */} {items.map((item, i) => ( <Button key={i} onClick={() => changeItem(i)}>{item.text}</Button> ))} </> );}// ...This was pure over-engineering. As you can see, I even defined a Map in the module scope to store the callbacks! The interviewer understandably asked:
âWhy a
Map? We arenât deleting items, so why didnât you just use a plain object?â
I didnât really have a good answer. Subconsciously, I remembered reading somewhere that SWR uses a Map for its internal cache, so I just blindly went with it. While Map is more optimized for highly dynamic structures with lots of additions and deletions, it was a terrible idea to spontaneously try using it in the middle of a live coding session without being comfortable with it.
The Final Solution: Thinking Outside the Box
Finally, the interviewer nudged me:
âCan you think outside the box for a second? Is there really no other way to solve this?â
I honestly didnât see the solution myself even with that nudge. In my panic, my mind was blank and I had forgotten everything. I definitely wasnât thinking outside the box.
Seeing my struggle, he hinted further:
âYou know, you can just pass the
setItemfunction that is returned from theuseStatedirectlyâŠâ
And then it suddenly clicked. Over my years of working with React, Iâve probably done this a million times in actual code. I just hadnât consciously thought about how setItem has a stable reference and how passing it directly optimizes the render.
I was about to write the code when he stopped me, letting me know that the interview was over.
Even though I didnât get to finish typing it out during the call, here is the incredibly simple and straightforward code that wouldâve come out of it. Instead of jumping through hoops, all it takes is passing setItem and index straight to the Button, and handling the execution inside the Button component:
{items.map((item, i) => ( <Button key={i} index={i} setItem={setItem}>{item.text}</Button> ))} </> ); }
const _Button: FC<PropsWithChildren<{ index: number; setItem: (idx: number) => void }>> = ({ index, setItem, children, }) => { console.log("RENDERING BUTTON"); return <button onClick={() => setItem(index)}>{children}</button>; };
const Button = React.memo(_Button);Because index is a primitive number, and setItem has a stable reference provided directly by useState, React.memo easily recognizes that nothing has changed.
It was such a ridiculously simple solution, and I felt a bit silly for not seeing it from the very beginning!
The TakeawayâŠ
It was a fascinating experience, and Iâm honestly glad I had this interview because I learned a lot from it.
One big realization: even if you have the experience and know all the concepts, if youâve been coding with AI too much lately, your âpure programmer musclesâ might atrophy a bit. Your fingers might not have the concepts down instantly even if your brain understands them at a high level.
I know that with a little more raw practice and remembering to just stay calm, itâs possible to have a much more successful run.