리액트 공식 문서에 따르면 고차 컴포넌트는 "컴포넌트 로직을 재사용하기 위한 React의 고급 기술"이라고 표현한다.
쉽게 표현하자면 기존 컴포넌트는 props를 받아 UI를 변환하는 역할을 한다면, 고차 컴포넌트 같은 경우에는 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수이다.
const EnhancedComponent = higherOrderComponent(WrappedComponent);
Custom hooks를 만들때 네이밍을 use로 시작하는것과 동일하게 고차 컴포넌트는 with로 시작해준다.
고차 컴포넌트를 활용한 todoList와 userList인지를 판별해 데이터를 넣어주는 함수형 예제로 보자
import './App.css';
import SerachTodos from './components/TodoList';
import SearchUsers from './components/UserList';
function App() {
return (
<div className="App">
<h2>higer order component</h2>
<div className="section">
<div>
<SearchUsers />
</div>
<div>
<SerachTodos />
</div>
</div>
</div>
);
}
export default App;
import React, { useEffect, useState } from 'react';
function withHOC(WrappedComponent, entity) {
return function () {
const [data, setData] = useState([]);
const [term, setTerm] = useState('');
useEffect(() => {
const timer = setTimeout(() => {
let fetchData =
entity === 'users'
? [
{ name: 'yeji', id: 1 },
{ name: 'yoyo', id: 2 },
{ name: 'jojo', id: 3 },
{ name: 'aoao', id: 4 },
]
: [
{ id: 1, userId: 1, title: 'hello', completed: true },
{ id: 2, userId: 2, title: 'bye', completed: true },
{ id: 3, userId: 3, title: 'mornign', completed: false },
{ id: 4, userId: 4, title: 'night', completed: false },
];
setData([...fetchData]);
clearTimeout(timer);
}, 2000);
}, []);
let filterData = data.filter((d) => {
if (entity === 'users') {
const { name } = d;
return name.indexOf(term) >= 0;
}
if (entity === 'todos') {
const { title } = d;
return title.indexOf(term) >= 0;
}
});
return (
<div>
<h2>{entity}</h2>
<input
type="text"
value={term}
onChange={(e) => setTerm(e.target.value)}
/>
<WrappedComponent data={filterData}></WrappedComponent>
</div>
);
};
}
export default withHOC;
import React, { useEffect, useState } from 'react';
import withHOC from './HOC';
function TodoLIst({ data }) {
let renderTodos = data.map((todo) => {
return (
<div key={todo.id}>
<p>
<strong>{todo.title}</strong>
</p>
</div>
);
});
return (
<div>
<div>{renderTodos}</div>
</div>
);
}
const SerachTodos = withHOC(TodoLIst, 'todos');
export default SerachTodos;
import React, { useEffect, useState } from 'react';
import withHOC from './HOC';
function UserList({ data }) {
let renderUsers = data.map((user) => {
return (
<div key={user.id}>
<p>
<strong>{user.name}</strong>
</p>
</div>
);
});
return (
<div>
<div>{renderUsers}</div>
</div>
);
}
const SearchUsers = withHOC(UserList, 'users');
export default SearchUsers;
HOC 패턴은 사실상 React에 hooks라는 개념이 나오기전에 재사용될만한 로직들을 간편하게 정리하기 위해 자주 쓰이던 패턴인듯 하다. 하지만 hooks가 등장하고 재사용 로직들이 많이 정돈되면서 대부분의 경우에서 HOC패턴은 hooks로 대처 가능하게 되었다.