Render Props 패턴은 JSX 엘리먼트를 props를 통해 컴포넌트에게 전달하는 방식으로 컴포넌트를 재사용 가능하게 할 수 있는 방법입니다.
Render prop
render prop은 컴포넌트 prop으로 JSX 엘리먼트를 리턴하는 함수를 전달합니다. 컴포넌트 자체는 아무 것도 렌더링하지 않지만 render prop 함수를 호출합니다.
<Title />은 render prop을 변경하며 재사용할 수 있습니다.
const Title = props => props.render()
// prop 이름이 꼭 render일 필요는 없다.
<Title render={() => <h1>✨ First render prop! ✨</h1>} />
<Title render={() => <h2>🔥 Second render prop! 🔥</h2>} />
<Title render={() => <h3>🚀 Third render prop! 🚀</h3>} />
render prop 함수를 호출할 때 인자를 전달할 수 있습니다.
function Component(props) {
const data = { ... }
// 내부 데이터를 전달할 수 있다.
return props.render(data)
}
<Component render={data => <ChildComponent data={data} />} />
상태 끌어올리기 방지
상태를 부모 컴포넌트로 끌어올릴 경우 부모 컴포넌트의 상태 변경은 모든 자식 컴포넌트의 리렌더링을 유발할 수 있고 앱의 전체적인 성능을 저하시킬 수 있습니다.
render prop을 사용하면 상태 끌어올리기를 방지할 수 있습니다.
function Input(props) {
const [value, setValue] = useState('')
return (
<>
<input
type="text"
value={value}
onChange={e => setValue(e.target.value)}
placeholder="Temp in °C"
/>
{props.children(value)}
</>
)
}
export default function App() {
return (
<div className="App">
<h1>☃️ Temperature Converter 🌞</h1>
<Input>
{value => (
<>
<Kelvin value={value} />
<Fahrenheit value={value} />
</>
)}
</Input>
</div>
)
}
특정 로직 재사용
검색한 결과를 리스트 형태로 보여주는 <SearchableList />를 객체, 문자열 등 모든 형태의 데이터에서 재사용하고 싶을 때 render prop을 사용할 수 있습니다.
데이터마다 렌더링할 JSX가 달라지기 때문에 render prop을 사용하여 인수로 내부 데이터를 받아 렌더링할 JSX를 컴포넌트 외부에서 정의할 수 있습니다.
import { useRef, useState } from 'react';
function SearchableList({ items, itemKeyFn, children }) {
const lastChange = useRef();
const [searchTerm, setSearchTerm] = useState('');
const searchResults = items.filter((item) =>
JSON.stringify(item).toLowerCase().includes(searchTerm.toLowerCase())
);
const handleChange = (event) => {
if (lastChange.current) {
clearTimeout(lastChange.current);
}
lastChange.current = setTimeout(() => {
lastChange.current = null;
setSearchTerm(event.target.value);
}, 500);
};
return (
<div className="searchable-list">
<input type="search" placeholder="Search" onChange={handleChange} />
<ul>
{searchResults.map((item) => (
<li key={itemKeyFn(item)}>{children(item)}</li>
))}
</ul>
</div>
);
}
export default SearchableList;
<SearchableList items={PLACES} itemKeyFn={(item) => item.id}>
{(item) => <Place item={item} />}
</SearchableList>
<SearchableList items={['item 1', 'item 2']} itemKeyFn={(item) => item}>
{(item) => item}
</SearchableList>
단점
- 컴포넌트 계층을 깊게 만들 수 있으며, 특히 여러 단계의 중첩된 Render Props를 사용할 경우 코드가 복잡해지고 이해하기 어려워질 수 있습니다.
- 함수를 자식 컴포넌트로 전달하기 때문에, 새로운 함수가 렌더링마다 생성됩니다. 이는 불필요한 재렌더링을 유발할 수 있어 성능에 영향을 미칠 수 있습니다.
- Render Props 패턴을 남용하면 JSX 코드가 길어지고 복잡해져 가독성이 떨어질 수 있습니다. 특히, 여러 개의 Render Props가 중첩되면 코드가 이해하기 어려워집니다.
'React' 카테고리의 다른 글
Storybook 알아보기 (1) | 2024.06.11 |
---|---|
Vite 기반 리액트 프로젝트와 Next.js 프로젝트에서 폰트 Preload 구현 (1) | 2024.06.05 |
핵심 웹 지표(Core Web Vitals) 정리 (0) | 2024.05.28 |
Redux와 Redux Toolkit으로 상태 관리하기 (0) | 2024.05.26 |
폰트 최적화 알아보기 (0) | 2024.05.25 |