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
- Tree: Render the data from
constant.jsonwith a recursiveListcomponent. Nested items are indented. - Expand and collapse: Clicking the arrow next to a folder toggles it between
▶and▼and shows or hides its children. All folders start closed. - 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.
- Delete: Every item has a delete icon that removes it, along with everything inside it.
- Cancel: The Cancel button closes the modal without changes.
Technical Constraints
- Update the tree immutably with a recursive
updateTreehelper, then callsetDatawith the new tree. - Each
Listlevel keeps its ownopenmap 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:
App.js- The main container componentList.js- A recursive component that renders the file/folder structure
The data structure is loaded from a JSON file (constant.json).
Code Flow
-
Initial Render:
- App component initializes with data from JSON file
- List component renders the initial file/folder structure
-
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
-
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
-
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
showModalto 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
updateTreeto find the parent folder and add the new item - Updates the data state with the new structure
- Closes the modal
- Core Logic: The
updateTreefunction 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
updateTreethat:- Filters out the item with the specified ID
- Recursively applies the same logic to all children
- Updates the data state with the filtered structure
- Uses a recursive function
- 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
openstate object
- Uses a functional state update to toggle the boolean value for the specified ID in the
- 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 renderaddItem: Function to add a new itemdeleteNode: 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:
-
Functional Components
- Both App and List are functional components using modern React patterns
- Example:
const App = () => { ... }andconst List = ({ list, addItem, deleteNode }) => { ... }
-
Hooks
- useState: Used to manage component state
- Examples:
const [data, setData] = useState(json)const [open, setOpen] = useState({})
-
Props
- Passing data and functions between components
- Example:
<List list={data} addItem={addItem} deleteNode={deleteNode} />
-
Conditional Rendering
- Rendering UI elements based on conditions
- Examples:
{showModal && (<div className="modal-overlay">...</div>)}{item.isFolder && (<span className="folder-icon">...</span>)}
-
Lists and Keys
- Proper key usage in mapped arrays
- Example:
<div key={item.id} className="item">...</div>
-
Recursive Components
- List component calls itself to render nested structures
- Example:
<List list={item.children} addItem={addItem} deleteNode={deleteNode} />
JavaScript Concepts:
-
Arrow Functions
- Compact function syntax used throughout the code
- Examples:
const toggleFolder = (id) => { ... }and() => setShowModal(false)
-
Destructuring
- Extracting values from objects/arrays
- Example:
const List = ({ list, addItem, deleteNode }) => { ... }
-
Spread Operator
- Creating new objects or arrays with modified properties
- Examples:
{ ...prev, [id]: !prev[id] }{ ...item, children: updateTree(item.children) }
-
Array Methods
map(): Transform array itemsfilter(): Remove items from an array- Examples:
list.map((item) => ( ... ))list.filter((item) => item.id !== itemId)
-
Functional State Updates
- Using previous state to calculate new state
- Example:
setOpen((prev) => ({ ...prev, [id]: !prev[id] }))
-
Recursion
- Functions that call themselves
- Examples:
updateTreefunction in bothhandleCreateItemanddeleteNode
-
Short-Circuit Evaluation
- Using
&&for conditional rendering - Example:
{open[item.id] && item.children && item.children.length > 0 && ( ... )}
- Using
-
Conditional (Ternary) Operator
- Compact if-else statements
- Example:
{open[item.id] ? "▼" : "▶"}
-
Template Literals
- String interpolation
- Example:
placeholder={`Enter ${newItemType} name`}
-
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:
- The
Appcomponent initializes withdatastate set to the contents ofconstant.json - The
Listcomponent receives this data and renders the top-level files and folders - All folders start in a collapsed state since the
openstate object is initially empty
Opening a Folder:
- User clicks the "▶" icon next to a folder
toggleFolder(id)is called with the folder's ID- The
openstate is updated to set that folder's ID totrue - When React re-renders, the condition
open[item.id]becomes true - The
Listcomponent recursively renders the children of that folder
Adding a New Item:
- User clicks the "+" icon on a folder
addItem(parentId)is called with the folder's ID- The function sets
currentParentIdand opens the modal - User selects a type (file or folder) and enters a name
- User clicks "Create"
handleCreateItem()is called- Inside this function,
updateTreerecursively searches for the parent folder - When found, it adds the new item to the parent's children array
setDataupdates the state with the new tree structure- React re-renders the UI with the new item
Deleting an Item:
- User clicks the delete icon on an item
deleteNode(itemId)is called with the item's ID- Inside this function,
updateTree:- Filters out the item with the matching ID
- Recursively applies the same process to all children
setDataupdates the state with the filtered tree- React re-renders the UI without the deleted item