Frontend interviews are getting more and more challenging day by day.
Implementing a complex frontend feature/UI live in an interview call is no joke.
I encountered one such situation where I had to build a VS Code like folder/file explorer UI.
This post will discuss the question, thought process, and system design behind it. Finally, we will write code to solve it.
What are we building?
Live preview with code: https://stackblitz.com/edit/vitejs-vite-4bqecn?file=src%2FApp.jsx
Let's start by understanding the philosophy and system design and then we will start writing code.
Before we begin, I want to mention that this post is part of my weekly newsletter.
Where I build such complex frontend features and solve complex frontend interview questions every week.
If you want to learn with me, join the Newsletter.
Now, let's continue.
Philosophy
The basic philosophy behind building a "Folder/File explorer UI" is as follows:
- List all the folders and files in the root directory.
- Clicking on the folder name should expand to show all the files inside that folder.
- Clicking on the folder again should collapse to hide all the files inside that folder.
- Clicking on a file would do nothing in our case.
CSS will not be the focus of this task.
System Design
We will create a JSON object to mimic a VS code-like folder/file structure. This object will contain all the folders and their nested folders/files.
Alright, but there is a big question here. How do we identify which entry in the JSON object is a folder and which is a file?
Well, we can keep nested folders/files in an array of objects and add the flag isFoloder
.
Here is our JSON object with all folders and files.
structure.js
const folderStructureData = {
name: 'root',
isFolder: true,
items: [
{
name: 'index.html',
isFolder: false,
},
{
name: 'app',
isFolder: true,
items: [
{
name: 'app.js',
isFolder: false,
},
{
name: 'src',
isFolder: true,
items: [
{
name: 'main.jsx',
isFolder: false,
},
{
name: 'utils.js',
isFolder: false,
},
],
},
{
name: 'app.css',
isFolder: false,
},
],
},
],
};
export default folderStructureData;
Now we need to access this JSON and render the folders/files name in UI. So let's do that.
Let the coding begin.
We'll start by fetching JSON data and map over it in App.jsx
. And at each iteration, we will check if the item is a folder or a file.
If it is a folder we will return <Folder />
component else <File />
component is returned.
App.jsx
import './App.css';
import Folder from './components/Folder';
import File from './components/File';
import folderStructureData from './structureData/structure.js';
function App() {
return (
<>
{folderStructureData.items.map((item) => {
return item.isFolder ? <Folder item={item} /> : <File item={item} />;
})}
</>
);
}
export default App;
Now, let's define our <Folder />
and <File />
components.
components/Folder.jsx
export default function Folder({ item }) {
return (
<>
<p
style={{
background: 'lightgray',
padding: '2px',
cursor: 'pointer',
}}
>
❭ {item.name}
</p>
</>
);
}
components/File.jsx
export default function File({ item }) {
return (
<p
style={{
background: 'lightgray',
padding: '2px',
}}
>
☵ {item.name}
</p>
);
}
Awesome, this renders our folders and files on screen. But wait ideally, we should see a folder structure like the following on screen.
---- index.html (file)
---- app (folder)
-------- app.js (file)
-------- src (folder)
------------ main.jsx (file)
------------ utils.js (file)
-------- app.css (file)
But what we see right now is just index.html
and app
, but why?
This is where this task becomes a little bit challenging. Can you figure it out on your own? Try and then come back.
Ok, let's solve it.
So basically, we are iterating over top-level data only in App.jsx
that's why it only shows top-level folder and file names on the screen. How do we iterate over the nested data? Like inside app
and then inside src
.
Well, we can manually do it for app
and src
but then it would fail if we change our JSON structure. Our solution should be such that it works automatically with any change in JSON structure.
Did you notice that our top-level and nested data both have the same data structure?
That design will allow us to recursively solve this problem. We will use the concept of recursion to solve this.
Recursion means calling a function within itself.
Ex:
function recurseEx() {
recurseEx();
}
Inside the Folder.jsx
we will again map but this time over nested data and check for isFolder
flag.
If isFolder
is false File.jsx
is returned, else Folder.jsx
is returned from Folder.jsx
itself.
Folder.jsx
import { useState } from 'react';
import File from './File';
export default function Folder({ item }) {
const [isExpanded, setIsExpanded] = useState(false);
return (
<>
<p
style={{
background: 'lightgray',
padding: '2px',
cursor: 'pointer',
}}
onClick={() => setIsExpanded((prevState) => !prevState)}
>
❭ {item.name}
</p>
<p
style={{
paddingLeft: '12px',
}}
>
{isExpanded &&
item.items.map((item) =>
item.isFolder ? <Folder item={item} /> : <File item={item} />
)}
</p>
</>
);
}
Notice, that the Folder.jsx
is returned from Folder.jsx
itself, and that is recursion. With that now all our nested data is taken care of, no matter how deeply they are nested.
There is also an isExpanded
state added, which just tracks if a folder is in expanded or collapsed state. And based on that the nested data of that folder is shown or hidden.
Now, try changing the JSON structure and our feature will still work. And that is awesome.
P.S:
This feature is not tough to build if you understand recursion. But I also know that recursion is not an easy concept to wrap our heads around.
And there are many more concepts like recursion. That's why I am building a resource that'll teach you such programming concepts with visual examples.
And don't worry it is not gonna cost you anything.
I am still working on that resource and am very close to launching it.
So, stay tuned by joining the Newsletter if you haven't already.
Thank you,
Comment down on which feature I should build next.
Top comments (4)
This is very awesome!! I tried it out before finishing reading and I got it working I did the hide/displaying of the folders a bit different I used styles with
display: open ? "block" : "none"
but yes the Function in the Function. that's amazing blog. Thankyou so much. I'm not sure I've ever called a component from inside it's self before.Thank you, Aimee,
Using
css display
property to show/hide folders/files is a fantastic way to handle this feature.It didn't come to my mind.
Thank you for sharing your approach.
really like it. Usually recursion is frightening, but here in you example you used it smoothly
Thank you for your kind words, Avraham. I am glad that you liked it.