Stand out in a React interview by rendering a list like a pro
Andrew Lee
Posted on November 13, 2022
We often get asked to render a list in a React interview. In this article we are going to look at a basic implementation and come up with four ways we can improve it to stand out from the rest.
Standard Implementation
Let's look at a basic implementation where we render a list based on an array of items. Try to think of at least three different ways we can improve the following implementation before reading further.
import { useState, useEffect } from "react";
const App = () => {
const [posts, setPosts] = useState([]);
const [currentPost, setCurrentPost] = useState(undefined);
useEffect(() => {
const initialize = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
const json = await res.json();
setPosts(json);
};
initialize();
}, []);
const onPostClick = (post) => {
setCurrentPost(post);
};
return (
<div>
{currentPost && <h1>{currentPost.title}</h1>}
<PostList posts={posts} onPostClick={onPostClick} />
</div>
);
};
const PostList = ({ posts, onPostClick }) => {
return (
<div>
{posts.map((post) => (
<Post post={post} onPostClick={onPostClick} />
))}
</div>
);
};
const Post = ({ post, onPostClick }) => {
const onClick = () => {
onPostClick(post);
};
return <div onClick={onClick}>{post.title}</div>;
};
export default App;
Improvements
Here are the four improvements that we can make to stand out. It’s important we articulate the why during the course of the interview.
1. Specify Key
By providing a key
prop to our list item components, we help React identify each item when it compares the original tree with its subsequent tree. It’s important to emphasize that the key
prop needs to be unique and we shouldn’t use an index as a key
(changing the order on the list doesn’t change the identity of the item).
{posts.map((post) => (
<Post key={post.id} post={post} onPostClick={onPostClick} />
))}
2. Optimize Rendering
Every time we click on a list item, we are re-rendering PostList
and every Post
.
const Post = ({ post, onPostClick }) => {
console.log("post rendered");
const onClick = () => {
onPostClick(post);
};
return <div onClick={onClick}>{post}</div>;
};
We can optimize our PostList
component by using the memo
function provided by React. When we wrap our component with memo
, we are telling React to not re-render this component unless the props have changed.
import { useState, useEffect, memo } from "react";
const PostList = memo(({ posts, onPostClick }) => {
return (
<div>
{posts.map((post) => (
<Post post={post} onPostClick={onPostClick} />
))}
</div>
);
});
However, we will notice that our component continues to re-render even with memo
. Our App
re-renders every time currentPost
state changes. Every re-render it is re-creating the onPostClick
function. When a function is re-created (even if it’s the same implementation), it has a new identity. Therefore, the props technically did change, which means PostList
will re-render.
const fn1 = () => {};
const fn2 = () => {};
fn1 === fn2; // => false
We can tell React to not re-create the function by using the useCallback
hook.
const onPostClick = useCallback((post) => {
setCurrentPost(post);
}, []);
Using useCallback
might have made sense in the previous example because it is preventing us from re-rendering all of the posts again. It’s important to point out that it doesn’t always make sense to wrap a function in a useCallback
.
const Post = ({ post, onPostClick }) => {
const useCalllback(onClick = () => {
onPostClick(post);
}, []);
return <div onClick={onClick}>{post.title}</div>;
};
We can point out to the interviewer that using the useCallback
in the Post
component might not make sense because the component in this case is lightweight. We should only use useCallback
if it makes sense (we can test by profiling). There are downsides to useCallback
; it increases the complexity of the code and calling useCallback
is additional code that gets run on every render.
3. Clean up when component un-mounts
Right now we are not doing any sort of clean up when the component un-mounts. For example, what if we decide to navigate away from the page before we get a response from our URL? We should cancel the request.
useEffect(() => {
const initialize = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
const json = await res.json();
setPosts(json);
};
initialize();
}, []);
useEffect
can be split up into two parts: code to run on mount and code to run on unmount:
useEffect(() => {
// When component mounts what code should I run?
return () => {
// When component unmounts what code should I run (clean up)?
};
}, []);
We can cancel the request by using the AbortController
and calling controller.abort()
on clean up.
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
const initialize = async () => {
try {
const res = await fetch("https://jsonplaceholder.typicode.com/posts", { signal });
const json = await res.json();
setPosts(json);
} catch (err) {
console.log(err);
}
};
initialize();
return () => {
controller.abort();
};
}, []);
4. Add accessibility
Final test that truly separates an exceptional candidate is if the candidate can talk about accessibility. Our sample application is too simple to add any tangible accessibility improvements, we should definitely talk about some things to look out for once our application grows in complexity. One test we can run is, can we use our sample application using the keyboard alone? One quick fix would be to convert items into buttons so that we can tab through them using our keyboard.
const Post = ({ post, onPostClick }) => {
const onClick = () => {
onPostClick(post);
};
return <button onClick={onClick}>{post}</button>;
};
Conclusion
Rendering a list in React seems like a simple interview question at first. Sometimes candidates might get frustrated why they didn’t pass the interview despite being able to implement a working solution. Next time we encounter this question, make sure to communicate to the interviewer (and implement them if given the time) the different ways we can render a list like a pro.
Posted on November 13, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.