본문 바로가기

FrontEnd

리액트 디자인패턴 : Render Props(렌더 프롭스 패턴)

반응형

TLDR :  렌더 프롭 패턴  = 컴포넌트 내 비즈니스 로직(캡슐화) + 비즈니스 로직 결과물을 이용한 렌더링

hook과 함께 렌더 프롭스 패턴은 죽었다는 의견이 많다.
하지만 아직 활용처가 남아있다는 사람이 있다.

특정 조건에서는 Render Props Pattern이 hooks보다 낫다는 것이다.
바로 선택적 렌더링이다.

렌더 프롭은 선택적으로 렌더 체인을 단락하고 다른 view로 일찍 반환할 수 있다.
즉 렌더 로직을 바이패스하는 서킷 브레이커와 같은 역할을 하는 컴포넌트들의 체인을 만들 수 있다.

either monad와 비슷하다고 보면 될 것이다.

DIY : 토큰을 통해 유저 정보 가져오기

사용자 id를 통해 토큰의 존재 여부를 판단한다.

type Token = string;
type Id = number;

const idTokenStore: { [id: Id]: Token } = {
  999: "Factos 👍 👀",
  222: "ㅜㅜ"
};

const TokenVisualizer = (token: Token) => <div>your token : {token}</div>;

렌더 프롭스 패턴을 구현한다. 아래와 같이 string인 토큰을 받아 하위 컴포넌트를 rendering하는 로직을 구현한다.

const fakeTokenStore = (id: Id) => idTokenStore[id];

const WithToken = ({
  id,
  children
}: {
  id: Id;
  children: React.FC<string>;
}) => {
  const token = fakeTokenStore(id);
  if (token) return children(token);
  else return <div> you have no authentication</div>;
};

children은 (token:string)=>React.ReactNode의 시그니처면 아무거나 다 가능하다.

export default function App() {
  return (
    <div className="App">
      <h1>Hello Render Props</h1>
      <WithToken id={999}>{(token) => TokenVisualizer(token)}</WithToken>
      <WithToken id={888}>{TokenVisualizer}</WithToken>
    </div>
  );
}

888은 스토어에 안넣어 뒀으므로 아래와 같이 보일 것이다.

해당 패턴은 중첩해서 적용할 수 있다.
id로 토큰을 가져왔으니, 토큰으로 사용자 정보를 가져와보자.

const exampleUser = { id: 999, name: "hyeoki" };

type User = typeof exampleUser;

const userStore: { [token: Token]: User } = {
  "Factos 👍 👀": exampleUser
};

사용자 정보를 보여주는 컴포넌트다.

const UserVisualizer = (user: User) => (
  <div>
    user id : {user.id} / user name : {user.name}
  </div>
);

사용자 정보 존재 여부에 따라 하위 컴포넌트를 렌더링한다.

const WithUser = ({
  children,
  token
}: {
  children: React.FC<User>;
  token: Token;
}) => {
  const user = getUser(token);
  if (user) return children(user);
  else return <div>no user info...</div>;
};

id가 없으면 WithToken 단계에서 단락,
user가 없으면 WithUser 단계에서 단락된다.

export default function App() {
  return (
    <div className="App">
      <h1>Hello Render Props</h1>
      {/* <WithToken id={999}>{(token) => TokenVisualizer(token)}</WithToken>
      <WithToken id={888}>{TokenVisualizer}</WithToken> */}
      <WithToken id={999}>
        {(token) => <WithUser token={token}>{UserVisualizer}</WithUser>}
      </WithToken>
      <WithToken id={888}>
        {(token) => <WithUser token={token}>{UserVisualizer}</WithUser>}
      </WithToken>
      <WithToken id={222}>
        {(token) => <WithUser token={token}>{UserVisualizer}</WithUser>}
      </WithToken>
    </div>
  );
}

컨테이너 패턴과 함께, 부수효과를 포함한 의존성 주입 (token을 외부에서 fetch) 등에 render props 패턴을 활용하는 것도 나쁘지 않을 것 같다.
특히 로그인 페이지로 강제 라우팅 한다거나 할 때 유용하게 사용할 수 있을 것으로 보인다.

 

패턴의 장점 : 사용자는 실패했을 경우를 전혀 신경쓰지 않고 happy path에 대해서 컴포넌트 렌더링 로직을 작성하면 된다.

else의 케이스는 Render Props 패턴을 구현한 컴포넌트가 알아서 처리해준다.
왜 Either 모나드랑 비슷하다고 했는지 이제 이해가 갈것이다.

 

렌더 프롭스 컴포넌트를 중첩해서 하나의 컴포넌트로 작성하는 것도 가능하다.

const WithTokenUser = ({
  children,
  id
}: {
  children: React.FC<User>;
  id: Id;
}) => {
  return (
    <WithToken id={id}>
      {(token) => <WithUser token={token}>{children}</WithUser>}
    </WithToken>
  );
};

 



심화된 예제를 보고싶으면 아래 링크를 참고한다.
https://paulgray.net/render-props-not-dead-yet/

 

Render Props; Not Dead Yet!

Render props still have a purpose in post-hooks react.

paulgray.net

추가 : 렌더 프롭을 사용하는 컴포넌트는 내부에 로직을 추상화한 컴포넌트다.

만약 오류가 발생할 경우, 그냥 위로 던지고 suspense에서 처리하는 것도 나쁘지 않다.(계층아키텍처)

참고

컨테이너 패턴

 

리액트 디자인 패턴 : 컨테이너(Contaner pattern) 패턴

원문 보기  데이터 가져오기, 인증, 레이아웃 등과 같은 사용자 정의 기능을 재사용하는 선언적 방법을 구현할 수 있습니다. 컨테이너 패턴이란? 사용자 지정 기능을 제공하고 children prop으로 전

itchallenger.tistory.com


컴포넌트의 관심사 분리하기

 

React 컴포넌트의 관심사의 분리

https://dmitripavlutin.com/orthogonal-react-components/ The Benefits of Orthogonal React Components How to design React components that are easy to change, maintain, and test? Strive to orthogonal R..

itchallenger.tistory.com

 

반응형