React Interview Task: Build a folder/file explorer UI.
Tags: react, interview, tutorial, javascript
Published: 2024-03-28 05:52:24

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](https://diaryofadev.ck.page).

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`_
```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`_
```js
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`
```js
export default function Folder({ item }) {
  return (
    <>
      <p
        style={{
          background: 'lightgray',
          padding: '2px',
          cursor: 'pointer',
        }}
      >
        &#x276D; {item.name}
      </p>
    </>
  );
}
```

_`components/File.jsx`_
```js
export default function File({ item }) {
  return (
    <p
      style={{
        background: 'lightgray',
        padding: '2px',
      }}
    >
      &#9781; {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`_
```js
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>
    </>
  );
}
```

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](https://diaryofadev.ck.page) if you haven't already.

----

Thank you,

Comment down on which feature I should build next.