React 파일 관리자를 만들어 봅시다. 7장: 사이드바 나열

31741 단어 typescriptmongezreact
이제 이전 기사에서 루트 디렉토리 내용을 로드한 것을 볼 수 있습니다. 이제 사이드바에 표시해 보겠습니다.

그러나 이 작업을 수행하기 전에 나중에 선택한 디렉토리를 로드하는 함수가 필요하므로 파일 관리자에서 디렉토리 로드를 개선해 보겠습니다. 따라서 load 구성 요소에 FileManager 함수를 생성해 보겠습니다.

// FileManager.tsx
...
  // load the given directory path
  const load = (path: string) => {
    setIsLoading(true);
    fileManager
      .load(path)
      .then(node => {
        setCurrentDirectoryNode(node);
      })
      .finally(() => {
        setIsLoading(false);
      });
  };

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

    setIsLoading(true);

    fileManager.load(rootPath).then(directoryNode => {
      setIsLoading(false);
      setCurrentDirectoryNode(directoryNode);
    });
  }, [rootPath, fileManager, open]);


로드 함수를 생성하고 이제 useEffect 후크에서 호출하여 루트 디렉토리를 로드해야 합니다.

// FileManager.tsx

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

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


이제 eslint는 종속성 배열에 없는 load 함수에 대해 불평할 것입니다. 따라서 이를 추가하지만 효과를 여러 번 다시 로드하지 않도록 useCallback로 래핑해야 합니다.

// FileManager.tsx

  // load the given directory path
  const load = useCallback(
    (path: string) => {
      setIsLoading(true);
      fileManager
        .load(path)
        .then(node => {
          setCurrentDirectoryNode(node);
        })
        .finally(() => {
          setIsLoading(false);
        });
    },
    [fileManager],
  );

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

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


한 가지 더 해야 할 일은 rootDirectoryNode 구성 요소에서 사용할 수 있도록 Sidebar를 상태로 저장해야 한다는 것입니다.

// FileManager.tsx
...
  const [currentDirectoryNode, setCurrentDirectoryNode] = useState<Node>();
  const [rootDirectoryNode, setRootDirectoryNode] = useState<Node>();
...
  // load the given directory path
  const load = useCallback(
    (path: string, isRoot = false) => {
      setIsLoading(true);
      fileManager
        .load(path)
        .then(node => {
          setCurrentDirectoryNode(node);
          if (isRoot) {
            setRootDirectoryNode(node);
          }
        })
        .finally(() => {
          setIsLoading(false);
        });
    },
    [fileManager],
  );

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

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



이제 FileManager는 다음과 같이 표시됩니다.

// FileManager.tsx
import { Grid, Modal } from "@mantine/core";
import BaseFileManager from "app/file-manager/utils/FileManager";
import { useCallback, useEffect, useRef, useState } from "react";
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 (
    <>
      <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: "/",
};


사이드바



이제 루트 디렉토리 내용을 표시해야 하므로 Sidebar 구성 요소를 업데이트하고 루트 노드에 전달해야 합니다.

// Sidebar.tsx
import { Card } from "@mantine/core";
import { Node } from "../../../types/FileManager.types";

export type SidebarProps = {
  rootDirectory?: Node;
};

export default function Sidebar({ rootDirectory }: SidebarProps) {
  return (
    <>
      <Card shadow="sm">
        <div>Sidebar</div>
      </Card>
    </>
  );
}


이제 하위 콘텐츠를 표시하기 위해 루트 디렉터리를 반복해 보겠습니다. 그러나 여기에는 문제가 있습니다. 루트 디렉터리인 경우에만 루트 디렉터리의 하위 항목을 표시해야 합니다.

// Sidebar.tsx
import { Card } from "@mantine/core";
import { useMemo } from "react";
import { Node } from "../../../types/FileManager.types";

export type SidebarProps = {
  rootDirectory?: Node;
};

export default function Sidebar({ rootDirectory }: SidebarProps) {
  const rootChildren = useMemo(() => {
    return rootDirectory?.children?.filter(child => child.isDirectory);
  }, [rootDirectory]);

  if (!rootDirectory) return null;

  return (
    <>
      <Card shadow="sm">
        <div>Sidebar</div>
      </Card>
    </>
  );
}


간단한 필터를 사용하여 루트 디렉토리 자식을 저장하고 isDirectory 속성이 true로 설정된 자식 노드만 가져오는 메모를 만듭니다.

이제 FileManager 구성 요소로 돌아가서 rootDirectoryNodeSidebar 구성 요소로 전달해 보겠습니다.

// FileManager.tsx

<Grid.Col span={3}>
-    <Sidebar />
+    <Sidebar rootDirectory={rootDirectoryNode} />
</Grid.Col>


이제 다음과 같은 내용이 표시됩니다.



사이드바 노드 표시



코드를 약간 개선하고 각 노드에 대한 사이드바 노드를 생성하여 더 많은 컨트롤을 얻을 수 있고 코드가 덜 복잡해집니다. 이름을 SidebarNode.tsx 로 지정하고 Sidebar 의 동일한 디렉토리에 생성합니다.

// SidebarNode.tsx
import { Node } from "../../../types/FileManager.types";

export type SidebarNodeProps = {
  node: Node;
};

export default function SidebarNode({ node }: SidebarNodeProps) {
  return (
    <>
      <div>{node.name}</div>
    </>
  );
}


이제 Sidebar 구성 요소를 업데이트하여 각 노드에 대해 SidebarNode를 표시해 보겠습니다.

// Sidebar.tsx
import { Card } from "@mantine/core";
import { useMemo } from "react";
import { Node } from "../../../types/FileManager.types";
import SidebarNode from "./SidebarNode";

export type SidebarProps = {
  rootDirectory?: Node;
};

export default function Sidebar({ rootDirectory }: SidebarProps) {
  const rootChildren = useMemo(() => {
    return rootDirectory?.children?.filter(child => child.isDirectory);
  }, [rootDirectory]);

  if (!rootDirectory) return null;

  return (
    <>
      <Card shadow="sm">
        {rootChildren?.map(child => (
          <SidebarNode key={child.path} node={child} />
        ))}
      </Card>
    </>
  );
}


이제 다음과 같이 표시됩니다.



다음 기사에서는 아이콘 추가와 같이 사이드바를 더 개선하고 사이드바 상단에 표시할 홈 루트도 추가할 것입니다.

기사 저장소



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

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



살람.

좋은 웹페이지 즐겨찾기