DEV Community

Cover image for React Interview Task: Build a folder/file explorer UI.
Swastik Yadav
Swastik Yadav

Posted on

React Interview Task: Build a folder/file explorer UI.

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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',
        }}
      >
        &#x276D; {item.name}
      </p>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

components/File.jsx

export default function File({ item }) {
  return (
    <p
      style={{
        background: 'lightgray',
        padding: '2px',
      }}
    >
      &#9781; {item.name}
    </p>
  );
}
Enter fullscreen mode Exit fullscreen mode

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)}
      >
        &#x276D; {item.name}
      </p>
      <p
        style={{
          paddingLeft: '12px',
        }}
      >
        {isExpanded &&
          item.items.map((item) =>
            item.isFolder ? <Folder item={item} /> : <File item={item} />
          )}
      </p>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
aimeetacchi profile image
Aimee

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.

Collapse
 
swastikyadav profile image
Swastik Yadav

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.

Collapse
 
avraham_hamu profile image
Avraham Hamu • Edited

really like it. Usually recursion is frightening, but here in you example you used it smoothly

Collapse
 
swastikyadav profile image
Swastik Yadav

Thank you for your kind words, Avraham. I am glad that you liked it.