Résumé (PDF)

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:

App.tsx
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:

App.tsx
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 setItem so that when a button is clicked, our item state 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.

App.tsx
{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:

App.tsx
// ...
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 setItem function that is returned from the useState directly
”

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:

App.tsx
{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.