Skip to content

File Explorer

Render a recursive folder tree where folders expand and collapse, and files and folders can be added or deleted.

Stage 4core6 min read

Problem Statement

Build a file explorer that shows a nested tree of files and folders from a JSON file. Folders open and close, and the user can add or delete items anywhere in the tree.

Functional Requirements

  1. Tree: Render the data from constant.json with a recursive List component. Nested items are indented.
  2. Expand and collapse: Clicking the arrow next to a folder toggles it between ▶ and ▼ and shows or hides its children. All folders start closed.
  3. Add: Each folder has an add icon that opens a modal. The user picks Folder or File, types a name and clicks Create. Empty names are ignored.
  4. Delete: Every item has a delete icon that removes it, along with everything inside it.
  5. Cancel: The Cancel button closes the modal without changes.

Technical Constraints

  • Update the tree immutably with a recursive updateTree helper, then call setData with the new tree.
  • Each List level keeps its own open map keyed by item id.
  • New items get id: Math.random(). Items can only be added inside a folder, not at the root.

Project Overview

This is a React implementation of a File/Folder Explorer similar to what you'd find in operating systems. The application allows users to:

  • View a hierarchical file/folder structure
  • Create new files or folders
  • Delete existing files or folders
  • Expand and collapse folders

Component Structure

The application consists of two main components:

  1. App.js - The main container component
  2. List.js - A recursive component that renders the file/folder structure

The data structure is loaded from a JSON file (constant.json).

Code Flow

  1. Initial Render:

    • App component initializes with data from JSON file
    • List component renders the initial file/folder structure
  2. User Interactions:

    • Clicking a folder arrow toggles its expanded/collapsed state
    • Clicking the add icon opens a modal to create a new item
    • Clicking the delete icon removes an item from the structure
  3. Creating New Items:

    • Modal collects information about the new item (name and type)
    • When confirmed, the new item is added to the data structure
    • UI rerenders to show the updated structure
  4. Deleting Items:

    • When a delete icon is clicked, the item is removed from the data structure
    • UI rerenders to reflect the change

Function Explanations in App Component:

1. addItem(parentId)

  • Purpose: Prepares the application to add a new item to a specific parent folder
  • Parameters:
    • parentId: ID of the folder where the new item will be added
  • Actions:
    • Sets the current parent ID in state
    • Resets the new item name to empty
    • Opens the modal by setting showModal to true
  • Used When: User clicks the add button on a folder

2. handleCreateItem()

  • Purpose: Creates a new file or folder and adds it to the data structure
  • Actions:
    • Validates that the name is not empty
    • Uses a recursive function updateTree to find the parent folder and add the new item
    • Updates the data state with the new structure
    • Closes the modal
  • Core Logic: The updateTree function traverses the tree to find the parent node by ID, then adds the new item to its children array
  • Used When: User confirms creation in the modal

3. deleteNode(itemId)

  • Purpose: Removes an item (file or folder) from the data structure
  • Parameters:
    • itemId: ID of the item to be deleted
  • Actions:
    • Uses a recursive function updateTree that:
      • Filters out the item with the specified ID
      • Recursively applies the same logic to all children
    • Updates the data state with the filtered structure
  • Used When: User clicks the delete button on an item

Function Explanations in List Component:

1. toggleFolder(id)

  • Purpose: Toggles the expanded/collapsed state of a folder
  • Parameters:
    • id: ID of the folder to toggle
  • Actions:
    • Uses a functional state update to toggle the boolean value for the specified ID in the open state object
  • Used When: User clicks on a folder's expand/collapse arrow

2. List Component (Recursive)

  • Purpose: Renders a list of files and folders, with nested folders rendered recursively
  • Props:
    • list: Array of file/folder objects to render
    • addItem: Function to add a new item
    • deleteNode: Function to delete an item
  • Key Logic:
    • Maps through the list of items to render each one
    • For folders, renders a toggle arrow and an add button
    • For all items, renders a delete button
    • If a folder is open and has children, recursively renders those children using the same List component
  • Used When: Initial render and whenever the data structure changes

React & JavaScript Concepts Used

React Concepts:

  1. Functional Components

    • Both App and List are functional components using modern React patterns
    • Example: const App = () => { ... } and const List = ({ list, addItem, deleteNode }) => { ... }
  2. Hooks

    • useState: Used to manage component state
    • Examples:
      • const [data, setData] = useState(json)
      • const [open, setOpen] = useState({})
  3. Props

    • Passing data and functions between components
    • Example: <List list={data} addItem={addItem} deleteNode={deleteNode} />
  4. Conditional Rendering

    • Rendering UI elements based on conditions
    • Examples:
      • {showModal && (<div className="modal-overlay">...</div>)}
      • {item.isFolder && (<span className="folder-icon">...</span>)}
  5. Lists and Keys

    • Proper key usage in mapped arrays
    • Example: <div key={item.id} className="item">...</div>
  6. Recursive Components

    • List component calls itself to render nested structures
    • Example: <List list={item.children} addItem={addItem} deleteNode={deleteNode} />

JavaScript Concepts:

  1. Arrow Functions

    • Compact function syntax used throughout the code
    • Examples: const toggleFolder = (id) => { ... } and () => setShowModal(false)
  2. Destructuring

    • Extracting values from objects/arrays
    • Example: const List = ({ list, addItem, deleteNode }) => { ... }
  3. Spread Operator

    • Creating new objects or arrays with modified properties
    • Examples:
      • { ...prev, [id]: !prev[id] }
      • { ...item, children: updateTree(item.children) }
  4. Array Methods

    • map(): Transform array items
    • filter(): Remove items from an array
    • Examples:
      • list.map((item) => ( ... ))
      • list.filter((item) => item.id !== itemId)
  5. Functional State Updates

    • Using previous state to calculate new state
    • Example: setOpen((prev) => ({ ...prev, [id]: !prev[id] }))
  6. Recursion

    • Functions that call themselves
    • Examples: updateTree function in both handleCreateItem and deleteNode
  7. Short-Circuit Evaluation

    • Using && for conditional rendering
    • Example: {open[item.id] && item.children && item.children.length > 0 && ( ... )}
  8. Conditional (Ternary) Operator

    • Compact if-else statements
    • Example: {open[item.id] ? "▼" : "▶"}
  9. Template Literals

    • String interpolation
    • Example: placeholder={`Enter ${newItemType} name`}
  10. Optional Chaining/Nullish Coalescing

    • Safely handling potentially undefined values
    • Example: ...(item.children || [])

Dry Run

Let's walk through a typical user flow to understand how the application works:

Initial Load:

  1. The App component initializes with data state set to the contents of constant.json
  2. The List component receives this data and renders the top-level files and folders
  3. All folders start in a collapsed state since the open state object is initially empty

Opening a Folder:

  1. User clicks the "▶" icon next to a folder
  2. toggleFolder(id) is called with the folder's ID
  3. The open state is updated to set that folder's ID to true
  4. When React re-renders, the condition open[item.id] becomes true
  5. The List component recursively renders the children of that folder

Adding a New Item:

  1. User clicks the "+" icon on a folder
  2. addItem(parentId) is called with the folder's ID
  3. The function sets currentParentId and opens the modal
  4. User selects a type (file or folder) and enters a name
  5. User clicks "Create"
  6. handleCreateItem() is called
  7. Inside this function, updateTree recursively searches for the parent folder
  8. When found, it adds the new item to the parent's children array
  9. setData updates the state with the new tree structure
  10. React re-renders the UI with the new item

Deleting an Item:

  1. User clicks the delete icon on an item
  2. deleteNode(itemId) is called with the item's ID
  3. Inside this function, updateTree:
    • Filters out the item with the matching ID
    • Recursively applies the same process to all children
  4. setData updates the state with the filtered tree
  5. React re-renders the UI without the deleted item

In this topic

Done with this topic?

How well do you know it?

PreviousProgress Bar