* 패턴은 완벽한 해결책이 아니다. 체계화 된 방법을 제시하는 것 뿐.
반드시 선택할 필요는 없고 상황에 맞게 설계해야 한다.
→ 리액트 컴포넌트 간에 함수를 공유하는 패턴

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이 각각 상황에 맞게 다른 방식으로 작동을 하고 있다.
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>
);
}
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>
);
}