DEV Community 👩‍💻👨‍💻

Cover image for Let's create a React File Manager Chapter IX: FileManager Hook And Context
Hasan Zohdy
Hasan Zohdy

Posted on

Let's create a React File Manager Chapter IX: FileManager Hook And Context

In our last article, we updated the sidebar node to show the directory name and icon, but we need first a way to get the file manager to be accessed from anywhere in the file manager components list, let's make a context for it.

FileManager Context

Let's make a context to capture current file manager easily.

Create contexts/FileManagerContext.tsx file and add the following code:

// contexts/FileManager.context.tsx
import { createContext } from "react";
import FileManager from "../utils/FileManager";

const FileManagerContext = createContext<FileManager | null>(null);

export default FileManagerContext;
Enter fullscreen mode Exit fullscreen mode

Now let's wrap our FileManager component with the context provider.

// FileManager.tsx
import { Grid, Modal } from "@mantine/core";
import BaseFileManager from "app/file-manager/utils/FileManager";
import { useCallback, useEffect, useRef, useState } from "react";
import FileManagerContext from "../../contexts/FileManagerContext";
import { Node } from "../../types/FileManager.types";
import Content from "./Content";
import { BodyWrapper } from "./FileManager.styles";
import { FileManagerProps } from "./FileManager.types";
import Sidebar from "./Sidebar";
import Toolbar from "./Toolbar";

export default function FileManager({
  open,
  onClose,
  rootPath,
}: FileManagerProps) {
  const [isLoading, setIsLoading] = useState(true);
  const [currentDirectoryNode, setCurrentDirectoryNode] = useState<Node>();
  const [rootDirectoryNode, setRootDirectoryNode] = useState<Node>();

  const { current: fileManager } = useRef(new BaseFileManager());

  // load the given directory path
  const load = useCallback(
    (path: string, isRoot = false) => {
      setIsLoading(true);

      fileManager.load(path).then(node => {
        setCurrentDirectoryNode(node);

        setIsLoading(false);
        if (isRoot) {
          setRootDirectoryNode(node);
        }
      });
    },
    [fileManager],
  );

  // load root directory
  useEffect(() => {
    if (!rootPath || !open) return;

    load(rootPath, true);
  }, [rootPath, fileManager, open, load]);

  return (
    <FileManagerContext.Provider value={fileManager}>
      <Modal size="xl" opened={open} onClose={onClose}>
        <Toolbar />
        <BodyWrapper>
          <Grid>
            <Grid.Col span={3}>
              <Sidebar rootDirectory={rootDirectoryNode} />
            </Grid.Col>
            <Grid.Col span={9}>
              <Content />
            </Grid.Col>
          </Grid>
        </BodyWrapper>
      </Modal>
    </FileManagerContext.Provider>
  );
}

FileManager.defaultProps = {
  rootPath: "/",
};
Enter fullscreen mode Exit fullscreen mode

File Manager hook

So we created the context, now let's create a simple hook to get the file manager from anywhere in the component.

Create hooks/useFileManager.ts file and add the following code:

import { useContext } from "react";
import FileManagerContext from "../contexts/FileManagerContext";

export default function useFileManager() {
  const fileManager = useContext(FileManagerContext);

  if (!fileManager) {
    throw new Error("useFileManager must be used within FileManagerProvider");
  }

  return fileManager;
}
Enter fullscreen mode Exit fullscreen mode

From this point we can now use the useFileManager hook to get the file manager from anywhere in any of our file manager components.

Now we are ready to point to active sidebar node, which will be done in the next chapter.

File Manager Public Properties

For quick access, we'll make the properties public to quickly access it.

// file-manager/utils/FileManager.ts
import fileManagerService from "../services/file-manager-service";
import { Node } from "../types/FileManager.types";

export default class FileManager {
  /**
   * Root path
   */
  public rootPath = "/";

  /**
   * Current directory path
   */
  public currentDirectoryPath = "/";

  /**
   * Current directory node
   */
  public currentDirectoryNode?: Node;

  /**
   * Set root path
   */
  public setRootPath(rootPath: string): FileManager {
    this.rootPath = rootPath;
    return this;
  }

  /**
   * Load the given path
   */
  public load(path: string): Promise<Node> {
    return new Promise((resolve, reject) => {
      fileManagerService
        .list(path)
        .then(response => {
          this.currentDirectoryPath = path;
          this.currentDirectoryNode = response.data.node;
          resolve(this.currentDirectoryNode as Node);
        })
        .catch(reject);
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

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)

DEV has this feature:

Settings

Go to your customization settings to nudge your home feed to show content more relevant to your developer experience level. 🛠