리액트를 사용하다 HOC에 대해 개념을 정리하고 가보자!
또한 HOC을 HOOK과 무슨 차이가 있는지 예제 코드 또한 아래에 나와있다.
HOC (Higher-order Component)
HOC는 고차 컴포넌트라고 부른다
리액트 공식 문서에 따르면 컴포넌트 로직을 재 사용하기 위한 고급 기술이라고도 하며
구체적으로는 HOC는 컴포넌트를 인자로 받아 새로운 컴포넌트로 반환하는 함수 이다
그렇기에 반복적인 코드 재사용이 용이하며 사용 방법으로는 HOC의 이름으로
주로 with이름 으로 규칙을 따른다
어? 어디서 많이봤는데 ??
그렇다 Routes.js가 아닌 컴포넌트에서 라우터를 사용하는 객체인 location, match, history를
사용하려면 withRouter라는 HOC를 사용해야 했었다 ( ?? 내가 이미 HOC를 썻잖아 ? )
또 다른 예로 많이 쓰이는 경우가 redux의 connect를 예로 들 수 있다
그리고 HOC의 경우에는 함수형 프로그래밍에서 사용하는 개념인 HOF와 유사하며
HOF의 경우 함수를 인자로 받아 새로운 함수를 반환하는 함수이다.
아래를 보자
let sum = a => async b => { return a + b; };
//위의 코드를 자세히 바꾼다면 아래와 같다
let sum = function(a) {
return async function(b) {
return a + b;
};
};
//함수를 반환한다 !!
HOC의 사용 예
function withProps(Comp, props) {
return function(ownProps) {
return <Comp {...props} {...ownProps} />
}
}
function Hello(props) {
return <div>Hello, {props.name}. I am {props.myName}</div>
}
const HelloJohn = withProps(Hello, { name: "John" });
const App = () => (
<div>
<HelloJohn myName="Kim" />
<HelloJohn myName="Lee" />
</div>
);
위의 코드를 보면 아~ 이렇게 사용하는구나를 알 수가 있다.
그렇다면 HOOK를 쓸 때와 HOC를 사용했을때의 차이를 아래의 코드로 확인해보자
const UserWithData = compose(
withFetch,
withFetch,
withError,
withLoading,
)(User);
const App = () => {
...
const userId = '1';
return (
<UserWithData
url={`https://api.mydomain/user/${userId}`}
url={`https://api.mydomain/user/${userId}/profile`}
/>
);
};
위의 코드는 두번의 url의 fetch를 위해 HOC를 사용하였다
하지만 동일한 props가 중복이 되어 있고 결국 후자만 유효하게 된다
그러면 그것을 예방하기 위해 withFetch의 url을 배열로 만약 만들게 되었다면
그게 정상적인 문제 해결 방법일까 ? ?
이 부분을 HOOKS로 해결하게 되면 아래와 같다
const App = () => {
const userId = '1';
const {
data: userData,
isLoading: userIsLoading,
error: userError
} = useFetch(`https://api.mydomain/user/${userId}`);
const {
data: userProfileData,
isLoading: userProfileIsLoading,
error: userProfileError
} = useFetch(`https://api.mydomain/user/${userId}/profile`);
if (userError || userProfileError) {
return <div>Something went wrong ...</div>;
}
if (userIsLoading) {
return <div>User is loading ...</div>;
}
const userProfile = userProfileIsLoading
? <div>User profile is loading ...</div>
: <UserProfile userProfile={userProfileData} />;
return (
<User
user={userData}>
userProfile={userProfile}
/>
);
};
useFetch라는 커스텀훅을 사용하였고
위에서 부터 차근차근 읽어나가다 보면 코드는 길어보이지만 이전에 있던 HOC의 코드에 비해
복잡하지도 않고 여러가지 조건별로 내가 행할 수 있는 처리 방식이 많아 졌다.
참고 링크 : 리액트 공식문서 / 고차 컴포넌트 사용하기 / HOC의 사용이 복잡해지는 경우