props.children 이용해서 레이아웃 만들기

이주희·2022년 3월 30일
0

CSS

목록 보기
7/11

1. props.children

  • 부모 컴포넌트로부터 props로 전달받은 값이 있는 태그의 자식 태그들은 자동으로 props.children이 된다!

  • props.children의 타입: ReactChild, ReactElement, ReactNode 등 다양하다. 예제에서는 JSX.Element를 사용했다.


1-1. children의 타입

ReactNode

  • jsx 내에서 사용할 수 있는 모든 요소의 타입을 의미한다.
  • string, null, undefined 등을 포함하는 가장 넓은 범위를 갖는 타입

ReactElement

  • 원시 타입을 허용하지 않고 완성된 jsx 요소만을 허용

ReactChild

  • ReactNode 타입을 적절히 내로잉(narrowing)한 타입
  • ReactElement 타입이 리액트 요소 객체만을 허용했다면, ReactChild 타입은 여기서 원시 타입까지는 허용한다.

JSX.Element

  • ReactElement 인터페이스를 상속받은 인터페이스로 ReactElement와 유사하다.

  • JSX.Element로 타입을 지정했을 때, children을 하나로 묶어주지 않으면 TS 에러가 발생한다.

  • ReactNode로 타입을 지정하면, children을 하나로 묶어주지 않아도 TS 에러가 발생하지 않는다.

1-2. 예시

props.children을 받는 컨테이너

interface IProps {
  children: JSX.Element;
}

export default function Example(props: IProps) {
  return (
    <>
      <header>Header</header>
      <div>{props.children}</div>
      <footer>Footer</footer>
    </>
  );
}

컨테이너 사용하기

  • 자식으로 전달될 태그들은 하나의 묶음이어야 한다.(태그가 여러 개일 경우, Fragment <> ~ </> 사용)
import Example from "../../src/components/units/13-props-children-example";

export default function PropsChildrenPAge() {
  return (
    <>
      <Example>
        <>
          {/* 자식 태그 */}
          <input type="text" />
          <button>버튼</button>
        </>
      </Example>
    </>
  );
}

2. Layout

UI가 모든 화면에 공통되어 고정시켜 놓는 부분을 레이아웃이라고 한다.

_app.tsx 파일에 설정을 입력하면 모든 파일에 적용이 된다.

header, nav 등을 미리 만들어놓은 컴포넌트를 Layout 만들고,
그 안에 변경되는 부분에 대한 컴포넌트를 넣는다.

props.children을 이용한다.


Layout 만들기

1.pages/_app.tsx

2. layout/index.tsx 생성

export default function Layout(props) {
  return (
    <>
      <div> 헤더 </div>
      <div> 배너 </div>
      <div> 내비게이션 </div>
      <div>{props.children}</div>
      {/* props.children : _app.tsx의 Layout의 자식 */}
    </>
  );
}

모든 화면에 Layout의 내용이 함께 ㄴㅏ온다


🔹 Layout의 각 항목 컴포넌트화 하기

3. 폴더 생성

4. 각 파일 생성

container / presenter 나눠도 된다.

5. layout/index.tsx에서 조립

import LayoutBanner from "./banner";
import LayoutFooter from "./footer";
import LayoutHeader from "./header";
import LayoutNavigation from "./navigation";
import styled from "@emotion/styled";
import { ReactNode } from "react";

const Body = styled.div`
  height: 500px;
`;

interface ILayoutProps {
  children: ReactNode;
}

export default function Layout(props: ILayoutProps) {
  return (
    <>
      <LayoutHeader />
      <LayoutBanner />
      <LayoutNavigation />
      <Body>{props.children}</Body>
      {/* props.children : _app.tsx의 Layout의 자식 */}
      <LayoutFooter />
    </>
  );
}



🔹 사이드바 만들기

6. LayoutSidebar 컴포넌트 추가

const BodyWrapper = styled.div`
  display: flex;
`;
const Body = styled.div`
  height: 200px;
`;
const LayoutSidebar = styled.div`
  width: 100px;
  height: 200px;
  background-color: orange;
`;

interface ILayoutProps {
  children: ReactNode;
}

export default function Layout(props: ILayoutProps) {
  return (
    <>
      <LayoutHeader />
      <LayoutBanner />
      <LayoutNavigation />
      <BodyWrapper>
        <LayoutSidebar>사이드바 영역</LayoutSidebar>
        <Body>{props.children}</Body>
      </BodyWrapper>
      <LayoutFooter />
    </>
  );
}


🔹 페이지마다 노출 항목 지정하기

7. 조건부 렌더링 추가

src/components/commons/layout/index.tsx

// HIDDEN_HEADERS: 숨길 페이지의 경로를 담은 배열
const HIDDEN_HEADERS = ["/12-05-modal-refactoring", "/12-01-modal-alert"];

export default function Layout(props: ILayoutProps) {
  
  const router = useRouter();
  const isHiddenHeader = HIDDEN_HEADERS.includes(router.asPath);
  
  return (
    <>
      {!isHiddenHeader && <LayoutHeader />}
    </>
  );
}

router

pathname과 route는 실제 값이 들어가있지 않은 경로
asPath는 실제 값이 들어간 경로

profile
🍓e-juhee.tistory.com 👈🏻 이사중

0개의 댓글