A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOCs are not part of the React API, per se. They are a pattern that emerges from React's compositional nature. Concretely, a higher-order component is a function that takes a component and returns a new component.
데이터를 fetch
하게 되면, 주로 아래와 같은 패턴이 사용된다.
function List({ dataStatus }) {
const { loading, error, data } = dataStatus;
if(loading) return <p>Loading...</p>
if(Error) return <p>Error occurs!</p>
return data.map(d => <div>...</div>)
}
한개의 컴포넌트에서 데이터를 fetch
한다면, 위와 같은 패턴을 사용하는 것이 문제가 없지만, 여러 컴포넌트에서 useFetch
와 같은 커스텀훅을 사용해서 fetch
를 한다면, 위의 패턴이 계속 반복된다. 우리는 알고있다. 반복되는 로직을 피해야한다는 것을!
그렇다면 어떻게 효율적으로 반복되는 로직을 줄일 수있을까? 🤓
➡️ HOC을 사용할 수 있다.
HOC을 생성할때는 withSomething
과 같은 이름을 사용한다.
WithLoading
을 생성
function WithLoading(Component) {
return function WihLoadingComponent({ dataObj, ...props }) {
const { loading, error } = dataObj;
if (loading) return <p>Loading...</p>;
if (error) return <p>Error</p>;
return <Component dataObj={dataObj} {...props} />;
};
}
export default WithLoading;
컴포넌트를 HOC
으로 래핑
import List1 from 'components/List1';
import List2 from 'components/List2';
const List1WithLoading = WithLoading(List1);
const List2WithLoading = WithLoading(List2);
function App() {
useFetch(ACTIVE_STATUS, 'activeStatus');
useFetch(INFORMATICS, 'informatics');
return(
<div>
<List1WithLoading dataObj={data1} title="Informatics" />
<List2WithLoading dataObj={data2} title="Active Status" />
</div>
)
}
위와 같이 useFetch
를 반복적으로 사용하고 싶지 않다면, HOC
안에 url
을 파라미터로 받아서 데이터를 fetch
하는 로직을 작성할 수 있다.
withFetch
const withFetch = (WrappedComponent, url) => {
const [data, setData] = useState([]);
const handleFetch = useCallback(async() => {
try {
const res = await fetch(url);
setData(res.data);
} catch(error) {
console.log(error)
}
});
useEffect(() => {
handleFetch();
}, [])
return <WrappedComponent data={data}/>
}
export default withFetch;
컴포넌트를 HOC
으로 래핑
function List() {
// ...
}
export default withFetch(List, "http://example.com")
import React, { ComponentType } from 'react';
import { Placeholder } from 'styles/styles';
import { DataState } from 'types/types';
interface WithLoadingProps {
dataObj: DataState;
}
function WithLoading<P extends object>(
Component: ComponentType<P>
): React.FC<P & WithLoadingProps> {
return function WihLoadingComponent({ dataObj, ...props }: WithLoadingProps) {
const { loading, error } = dataObj;
if (loading) return <Placeholder>Loading...</Placeholder>;
if (error) return <Placeholder>Error</Placeholder>;
return <Component dataObj={dataObj} {...(props as P)} />;
};
}
export default WithLoading;