React 파일 관리자를 만들어 봅시다. 6장: 디렉터리 및 파일 가져오기

32168 단어 typescriptmongezreact
이제 몇 가지 논리 작업을 시작하고 먼저 작업 흐름을 정의해 보겠습니다.

워크플로우



파일 관리자가 열리면 root 경로를 로드한 다음 사이드바에 해당 디렉토리를 나열하고 오른쪽에 해당 내용(파일 및 디렉토리)을 나열합니다.

따라서 다음 상태가 필요합니다.
  • Loading 파일 관리자가 디렉토리를 로드 중인지 확인하는 상태입니다.
  • currentDirectoryNode : 현재 로드된 디렉토리 노드를 포함하는 상태입니다.

  • 지금은 그게 다입니다. 실제로 작동하는 것을 봅시다.

    // FileManager.tsx
    
    export default function FileManager({
      open,
      onClose,
      rootPath,
    }: FileManagerProps) {
      const [isLoading, setIsLoading] = useState(true);
      const [currentDirectoryNode, setCurrentDirectoryNode] = useState<Node>();
    
      return (
        <>
          <Modal size="xl" opened={open} onClose={onClose}>
            <Toolbar />
            <BodyWrapper>
              <Grid>
                <Grid.Col span={3}>
                  <Sidebar />
                </Grid.Col>
                <Grid.Col span={9}>
                  <Content />
                </Grid.Col>
              </Grid>
            </BodyWrapper>
          </Modal>
        </>
      );
    }
    
    FileManager.defaultProps = {
      rootPath: "/",
    };
    


    기본 props 객체에 rootPath를 추가하고 앞에서 언급한 것처럼 두 가지 상태를 생성했습니다.

    이제 파일 관리자의 새 인스턴스를 만들어 ref에 저장해 보겠습니다.

    import { Grid, Modal } from "@mantine/core";
    import BaseFileManager from "app/file-manager/utils/FileManager";
    import { useRef, useState } from "react";
    import Content from "./Content";
    import { BodyWrapper } from "./FileManager.styles";
    import { FileManagerProps } from "./FileManager.types";
    import Sidebar from "./Sidebar";
    import Toolbar from "./Toolbar";
    import { Node } from "../../types/FileManager.types";
    
    export default function FileManager({
      open,
      onClose,
      rootPath,
    }: FileManagerProps) {
      const [isLoading, setIsLoading] = useState(true);
      const [currentDirectoryNode, setCurrentDirectoryNode] = useState<Node>();
    
      const fileManagerRef = useRef(new BaseFileManager());
    
      return (
        <>
          <Modal size="xl" opened={open} onClose={onClose}>
            <Toolbar />
            <BodyWrapper>
              <Grid>
                <Grid.Col span={3}>
                  <Sidebar />
                </Grid.Col>
                <Grid.Col span={9}>
                  <Content />
                </Grid.Col>
              </Grid>
            </BodyWrapper>
          </Modal>
        </>
      );
    }
    
    FileManager.defaultProps = {
      rootPath: "/",
    };
    


    이름을 BaseFileManager로 변경하여 구성 요소 자체와 혼동하지 않도록 했습니다.

    실제로 current 키를 직접 파괴하여 ref를 향상시킬 수 있습니다.

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


    다음으로 루트 경로를 로드할 useEffect 후크를 생성해 보겠습니다.

    import { Node } from "../../types/FileManager.types";
    
    export default function FileManager({
      open,
      onClose,
      rootPath,
    }: FileManagerProps) {
      const [isLoading, setIsLoading] = useState(true);
      const [currentDirectoryNode, setCurrentDirectoryNode] = useState<Node>();
    
      const { current: fileManager } = useRef(new BaseFileManager());
    
      // load root directory
      useEffect(() => {
        if (!rootPath) return;
    
        setIsLoading(true);
    
        fileManager.load(rootPath).then(() => {
          setIsLoading(false);
          setCurrentDirectoryPath(rootPath);
        });
      }, [rootPath, fileManager]);
    
      return (
        <>
          <Modal size="xl" opened={open} onClose={onClose}>
            <Toolbar />
            <BodyWrapper>
              <Grid>
                <Grid.Col span={3}>
                  <Sidebar />
                </Grid.Col>
                <Grid.Col span={9}>
                  <Content />
                </Grid.Col>
              </Grid>
            </BodyWrapper>
          </Modal>
        </>
      );
    }
    
    FileManager.defaultProps = {
      rootPath: "/",
    };
    


    이제 rootPath를 종속성 배열에 추가했으므로 useEffect가 변경되면 rootPath가 호출되고 지정된 경로를 현재 디렉터리로 로드하므로 load도 호출됩니다.
    load 메서드로 이동하기 전에 파일 관리자가 열려 있지 않으면 로드를 무시한다는 또 다른 조건을 추가해 보겠습니다.

      // load root directory
      useEffect(() => {
        if (!rootPath || !open) return;
    
        setIsLoading(true);
    
        fileManager.load(rootPath).then(directoryNode => {
          setIsLoading(false);
          currentDirectoryNode(directoryNode);
        });
      }, [rootPath, fileManager, open]);
    


    로드 방법 생성


    load 메서드는 지정된 경로를 로드하고 경로가 로드되면 해결될 약속을 반환하며 로드된 디렉토리와 함께 Node도 반환합니다.

    또한 파일 관리자에서 루트 경로를 정의해야 하므로 setRootPath 메서드를 생성합니다.

    
    // file-manager/utils/FileManager.ts
    import { Node } from "../types/FileManager.types";
    
    export default class FileManager {
      /**
       * Root path
       */
      protected rootPath = "/";
    
      /**
       * Current directory path
       */
      protected currentDirectoryPath = "/";
    
      /**
       * Current directory node
       */
      protected currentDirectoryNode?: Node;
    
      /**
       * Set root path
       */
      public setRootPath(rootPath: string): FileManager {
        this.rootPath = rootPath;
        return this;
      }
    }
    

    FileManagerService로 이동하여 여기에서 새 인스턴스를 만들고 내보내서 직접 사용할 수 있도록 합시다.

    파일 끝에서 인스턴스를 내보냅니다.

    // file-manager-service.ts
    const fileManagerService = new FileManagerService();
    
    export default fileManagerService;
    


    이제 load에서 FileManager 메서드를 정의해 보겠습니다.

    // FileManager.ts
    import fileManagerService from "../services/file-manager-service";
    import { Node } from "../types/FileManager.types";
    
    export default class FileManager {
      /**
       * Root path
       */
      protected rootPath = "/";
    
      /**
       * Current directory path
       */
      protected currentDirectoryPath = "/";
    
      /**
       * Current directory node
       */
      protected 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);
        });
      }
    }
    

    list에서 FileManagerService 메서드를 사용하여 디렉터리 노드를 가져온 다음 currentDirectoryNode에 저장하고 반환했습니다.

    그러나 list 메서드를 약간 수정해야 합니다. 백엔드에서 직접 내부에 나열된 모든 자식을 포함하는 단일 노드를 반환해야 백엔드에서 처리할 수 있습니다.

    import FileManagerServiceInterface from "../types/FileManagerServiceInterface";
    import { 
    +    newNode, 
    -    listNodes
         } from "../utils/data";
    
    export class FileManagerService implements FileManagerServiceInterface {
      /**
       * {@inheritDoc}
       */
      public list(directoryPath: string): Promise<any> {
        return new Promise(resolve => {
          resolve({
            data: {
    -          node: listNodes(),
    +          node: newNode(),
            },
          });
        });
      }
    }
    
    const fileManagerService = new FileManagerService();
    
    export default fileManagerService;
    

    load 메서드로 돌아가서 로드 약속을 위해 확인될 현재 디렉터리 노드도 정의했습니다.

    이제 FileManager.tsx 구성 요소와 console.logcurrentDirectoryNode로 이동하여 무엇이 반환되는지 확인합니다.

      ...
      const { current: fileManager } = useRef(new BaseFileManager());
    
      console.log(currentDirectoryNode);
    


    무작위로 생성되기 때문에 항상 디렉토리가 있는지 확인하기 위해 디렉토리에 대해 또 다른 생성 노드를 만들어 봅시다.

    // utils/data.ts
    
    export function newNode(): Node {
      const isDirectory = faker.datatype.boolean();
      const node: Node = {
        name: isDirectory ? faker.system.directoryPath() : faker.system.fileName(),
        path: faker.system.filePath(),
        size: faker.datatype.number({ min: 1, max: 100000 }),
        isDirectory,
      };
    
      if (node.isDirectory) {
        node.children = listNodes(1, 3);
      }
    
      return node;
    }
    
    export function newDirectoryNode() {
      const node = newNode();
      node.children = listNodes(faker.datatype.number({ min: 3, max: 4 }), 5);
      node.name = faker.system.directoryPath();
      node.isDirectory = true;
      return node;
    }
    


    또한 listNodes에서 최소값과 최대값을 허용하도록 약간 수정하여 항상 최소 3개의 하위 항목이 있는 디렉토리를 갖도록 했습니다.

    이제 newDirectoryNode 대신 newNode를 호출하도록 서비스 클래스를 업데이트하겠습니다.

    - import { newNode } from "../utils/data";
    + import { newDirectoryNode } from "../utils/data";
    
        return new Promise(resolve => {
          resolve({
            data: {
    -          node: newNode(),
    +          node: newDirectoryNode(),
            },
          });
        });
    


    여기에서 잠시 멈추고 다음 기사에서 계속하겠습니다. 헷갈린다면 아래 리포지토리에서 최종 코드를 읽은 다음 기사를 다시 읽으십시오.

    다음 기사는 사이드바에 디렉토리를 나열하는 방법에 관한 것입니다.

    기사 저장소



    Github Repository에서 챕터 파일을 볼 수 있습니다.

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



    살람.

    좋은 웹페이지 즐겨찾기