DEV Community

Cover image for Let's create a React File Manager Chapter XIII: Organizing files
Hasan Zohdy
Hasan Zohdy

Posted on

Let's create a React File Manager Chapter XIII: Organizing files

This chapter is about organizing files in a way that makes more sense and easy to understand.

It will also be a light one, and we'll be playing around in everywhere.

Let's do some colors first

Before we jump to cleaning the code, let's add some fancy colors to our LoadingProgressBar component, also let's add label to indicate the current loading percentage.

// LoadingProgressBar.tsx
import { Progress } from "@mantine/core";
import { useEffect, useState } from "react";
import useFileManager from "../../hooks/useFileManager";

export default function LoadingProgressBar() {
  const fileManager = useFileManager();
  const [progress, setProgress] = useState(0);

  useEffect(() => {
    // let's create an interval that will update progress every 300ms
    let interval: ReturnType<typeof setInterval>;

    // we'll listen for loading state
    const loadingEvent = fileManager.on("loading", () => {
      setProgress(5);

      interval = setInterval(() => {
        // we'll increase it by 10% every 100ms
        // if it's more than 100% we'll set it to 100%
        setProgress(progress => {
          if (progress >= 100) {
            clearInterval(interval);
            return 100;
          }

          return progress + 2;
        });
      }, 100);
    });

    // now let's listen when the loading is finished
    const loadEvent = fileManager.on("load", () => {
      // clear the interval
      setProgress(100);

      setTimeout(() => {
        clearInterval(interval);

        // set progress to 0
        setProgress(0);
      }, 300);
    });

    // unsubscribe events on unmount or when use effect dependencies change
    return () => {
      loadingEvent.unsubscribe();
      loadEvent.unsubscribe();
    };
  }, [fileManager]);

  const mapProgressColor = () => {
    if (progress < 25) {
      return "blue";
    }

    if (progress < 50) {
      return "indigo";
    }

    if (progress < 75) {
      return "lime";
    }

    return "green";
  };

  return (
    <Progress
      size="lg"
      value={progress}
      striped
      label={progress > 0 ? `${progress}%` : undefined}
      color={mapProgressColor()}
      animate
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

I'll not say much here, the code is self-explanatory.

Encapsulations

As we saw over the previous chapters, we added more components, and some of these components have their own types and styles, so let's create a directory for each of these components.

We will export all components that are located in src/apps/front-office/file-manager/components/FileManager to be directly in components directory.

Relative Paths

Before we do this, we need to change all relative paths that are outside of current directory to be absolute paths.

For example, File Manager types.

Before


// FileManager.types.ts
import { Node } from "../../types/FileManager.types";

export type FileManagerProps = {
  /**
   * Root path to open in the file manager
   *
   * @default "/"
   */
  rootPath?: string;
  ...
};
Enter fullscreen mode Exit fullscreen mode

After

// FileManager.types.ts
import { Node } from "app/file-manager/types/FileManager.types";

export type FileManagerProps = {
  /**
   * Root path to open in the file manager
   *
   * @default "/"
   */
  rootPath?: string;
  ...
};
Enter fullscreen mode Exit fullscreen mode

And so on for the rest of the files.

I don't really like relative paths, let's make it absolute, i'll skip it in the article but you can see it in the repository's branch.

Sidebar

Let's start with Sidebar component.

Create Sidebar.types.ts file and move all types related to Sidebar component to it.

We need to move it to be directly in components directory.

// Sidebar.types.ts
import { Node } from "../../../types/FileManager.types";

export type SidebarProps = {
  rootDirectory?: Node;
};
Enter fullscreen mode Exit fullscreen mode

Now we remove the type block from the Sidebar component and just import our new type.

We will also create a separate directory for SidebarNode but it will be inside Sidebar directory.

Now the final structure will look like this:

[File Structure]

Kernel

As we have two identical names, the FileManager that makes all operations and FileManager component, we need to rename the FileManager component to Kernel.

We will also move all types related to Kernel to Kernel.types.ts file.

We'll create a directory for it and move all related files to it.

We also will rename the context to be KernelContext and the hook to be useKernel.

Now we'll go through all files and update it properly.

I advice you to see it in the repository so you know what you'll update.

Indexes for each directory

If we checked the FileManager component imports, we'll see it after previous modifications as follows:

Imports

import { Grid } from "@mantine/core";
import Content from "app/file-manager/components/Content";
import LoadingProgressBar from "app/file-manager/components/LoadingProgressBar";
import Sidebar from "app/file-manager/components/Sidebar";
import Toolbar from "app/file-manager/components/Toolbar";
import KernelContext from "app/file-manager/contexts/KernelContext";
import Kernel, { Node } from "app/file-manager/Kernel";
Enter fullscreen mode Exit fullscreen mode

ٍSo components have multiple levels, contexts hooks and kernel itself.

We can make it easier by creating an index.ts file in each directory and export all components from it.

// hooks/index.ts
export { default as useKernel } from "./useKernel";
export { default as useLoading } from "./useLoading";
Enter fullscreen mode Exit fullscreen mode
// contexts/index.ts
export { default as KernelContext } from "./KernelContext";
Enter fullscreen mode Exit fullscreen mode
// components/index.ts
export { default as Content } from "./Content";
export { default } from "./FileManager";
export * from "./Sidebar";
export { default as Sidebar } from "./Sidebar";
export { default as Toolbar } from "./Toolbar";
Enter fullscreen mode Exit fullscreen mode

Now we can directly call our imports like this

import { useLoading } from "app/file-manager/hooks";
import { Node } from "app/file-manager/Kernel";
Enter fullscreen mode Exit fullscreen mode

Top index for the entire file manager

Let's create an index.ts file in app/file-manager directory and export all components from it.

// app/file-manager/index.ts
export * from "./components";
export { default } from "./components";
export * from "./contexts";
export * from "./hooks";
export * from "./Kernel";
export { default as Kernel } from "./Kernel";
Enter fullscreen mode Exit fullscreen mode

Be Careful with the internal imports and circular dependencies

Don't make all imports from the index files as some files will have circular dependencies, for example in FileManager component we can not call LoadingProgressBar from components/index but we have to call it directly `app/front-office/file-manager/components/LoadingProgressBar.

Finally let's update our import in HomePage component

`tsx
// HomePage.tsx
import Helmet from "@mongez/react-helmet";
👉🏻 import FileManager from "app/file-manager";

export default function HomePage() {
return (
<>

👉🏻
</>
);
}
`

Now we're gooooood, everything is clean and shiny.

Article Repository

You can see chapter files in Github Repository

Don't forget the main branch has the latest updated code.

Tell me where you are now

If you're following up with me this series, tell me where are you now and what you're struggling with, i'll try to help you as much as i can.

Salam.

Top comments (0)