[React] Render Props 패턴

Empu·2024년 9월 22일

react

목록 보기
2/6

패턴이란?

  • 소프트웨어 설계에서 반복되는 문제와 주제에 적용할 수 있는 재사용 가능한 템플릿.

패턴이 유용한 이유

  • 검증되었다 : 앞서간 개발자들의 경험과 통찰의 산물. 문제를 해결하기 위해 오랜시간 동안 검증된 효과적인 접근 방식
  • 쉽게 재사용 가능: 독창적인 솔루션을 제공하며 사용자의 요구에 맞춰 적용 가능.
  • 알아 보기 쉽다: 정해진 구조와 공통 표현을 사용하여 광범위한 문제에 대한 정규한 솔루션 제공
  • 사소한 실수로 인해 생길수 있는 큰 문제 방지
  • 반복을 피함으로서 전체 코드의 양을 줄일 수 있다(특정 패턴 제외)

* 패턴은 완벽한 해결책이 아니다. 체계화 된 방법을 제시하는 것 뿐.
반드시 선택할 필요는 없고 상황에 맞게 설계해야 한다.

RenderProps Pattern

→ 리액트 컴포넌트 간에 함수를 공유하는 패턴

패턴 적용 이유

  • prop을 받는 컴포넌트의 재사용이 편리하다.
  • 동일한 렌더링 방식이 연속적으로 사용되는 컴포넌트에서 아주 유용 (리스트, 테이블 형태)

상태 끌어올리기 방지

  • 상태를 부모 컴포넌트로 끌어올릴 경우 부모 컴포넌트의 상태 변경은 모든 자식 컴포넌트의 리렌더링을 유발할 수 있고 앱의 전체적인 성능을 저하시킬 수 있다 → render props 패턴

const PLACES = [
  {
    id: 'african-savanna',
    image: savannaImg,
    title: 'African Savanna',
    description: 'Experience the beauty of nature.',
  },
  {
    id: 'amazon-river',
    image: amazonImg,
    title: 'Amazon River',
    description: 'Get to know the largest river in the world.',
  },
  {
    id: 'caribbean-beach',
    image: caribbeanImg,
    title: 'Caribbean Beach',
    description: 'Enjoy the sun and the beach.',
  },
  {
    id: 'desert-dunes',
    image: desertImg,
    title: 'Desert Dunes',
    description: 'Discover the desert life.',
  },
  {
    id: 'forest-waterfall',
    image: forestImg,
    title: 'Forest Waterfall',
    description: 'Listen to the sound of the water.',
  },
];

<section>
    <SearchableList items={PLACES} itemKeyFn={(item) => item.id}>
      {(item) => <Place item={item} />}
    </SearchableList>
    <SearchableList items={['items1', 'items2']} itemKeyFn={(item) => item}>
      {(item) => item}
    </SearchableList>
</section>

SearchableList 컴포넌트 내부에서 리스트를 관리하는 로직은 같지만, 어떻게 렌더링할지는 외부에서 결정할 수 있도록 유연하게 설계되었다

SearchableList 컴포넌트는 재사용 가능하고, 각기 다른 UI 요구 사항에 맞게 확장할 수 있다

itemKeyFn이 각각 상황에 맞게 다른 방식으로 작동을 하고 있다.

  • SearchableList.tsx
import { useState } from 'react';

export default function SearchableList({ items, itemKeyFn, children }) {
  const [searchTerm, setSearchTerm] = useState('');

  const searchResults = items.filter((item) =>
    JSON.stringify(item).toLowerCase().includes(searchTerm.toLowerCase()),
  );

  const handleChange = (e) => {
    setSearchTerm(e.target.value);
  };
  return (
    <div className="searchable-list">
      <input
        type="search"
        placeholder="Search"
        onChange={handleChange}
        value={searchTerm}
      />
      <ul>
        {searchResults.map((item, index) => (
          <li key={itemKeyFn(item)}>{children(item)}</li>
        ))}
      </ul>
    </div>
  );
}
  • Place.tsx
export default function Place({ item }) {
  return (
    <article className="place">
      <img src={item.image} alt={item.title} />
      <div>
        <h2>{item.title}</h2>
        <p>{item.description}</p>
      </div>
    </article>
  );
}

단점

  • 보통 render props로 해결하려는 부분은 hook으로 대체 가능하다
profile
Life is a risk.

0개의 댓글