만족

[React] Hook과 함께 HOC 사용해보기 본문

[React] Hook과 함께 HOC 사용해보기

FrontEnd/React Satisfaction 2020. 11. 4. 20:44

HOC가 뭘까?

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.

HOC는 리액트에서 컴포넌트 로직 재사용을 위해 사용하는 고급 기술입니다.
HOC는 React API의 일부가 아니며, 단지 리액트의 집합 특성에서 나온 패턴입니다.
정확히 말해, HOC는 어떤 컴포넌트를 사용해 새로운 컴포넌트를 리턴하는 함수입니다.

리액트 공식 문서에서는 다음과 같이 설명하고 있는데, 마지막 줄이 HOC의 핵심이다.

 

먼저 HOC가 왜 필요한지, 어떨 때 사용하는지를 알아보자.

 

React를 사용해 개발하다 보면, 특정 데이터에 대한 로딩 부분의 함수가 매우 빈번하게 중복된다.

 

내가 운영중인 사이트를 예시로 보면

 

차례로 캐릭터 정보 페이지/플레이어 전적에서 캐릭터 필터링/캐릭터 아이템 통계 페이지인데,

셋 다 캐릭터 리스트를 로드하는 로직이 필요하기 때문에 코드 중복이 발생한다.

 

캐릭터 리스트를 state로 갖는 컴포넌트를 두고 재사용하면 되지 않느냐 할 수도 있는데, 

필요한 것은 캐릭터 리스트를 표시하는 UI가 아니라 

'캐릭터 리스트를 불러오는 로직' 또는 '캐릭터 리스트'였기 때문에 불가능하다.

 

이와 같이 동일한(혹은 재사용 가능한) "컴포넌트 로직(이 경우에는 캐릭터 리스트 로딩)"에

HOC 한방이면 모두 해결 가능하다.

 

HOC 만들어보기

우선 네이밍 룰은 with로 시작하는게 관행이다.

 

내 경우에는 캐릭터(사이퍼) 리스트이므로, withCypherList를 사용할거다.

const withCypherList = WrappedComponent => {
  const Component = props => {
    return <WrappedComponent {...props} />;
  };
  return Component;
};

export default withCypherList;

기본적으로 hoc는 다음과 같은 형태를 갖는다.

 

맨 윗줄에서 말했듯이, hoc는 어떤 컴포넌트를 가지고 새로운 컴포넌트를 반환하는 함수이기 때문에

withCypherList는 컴포넌트를 받아, 새로운 컴포넌트를 반환하는 이중 함수의 형태로 되어있다. 

 

이제 중복되는 로직인 "캐릭터 리스트 로딩"을 추가해보자.

const withCypherList = WrappedComponent => {
  const Component = props => {
    //중복 로직 시작
    //중복 로직 시작
    //중복 로직 시작
    const [cypherList, setCypherList] = useState();
    useEffect(() => {
    	getCharacter()
            .then(res => {
              const { characters } = res.data;
              setCypherList(characters);
              console.log("cyphers", cypherList);
            })
            .catch(e => {
              console.log(e);
            });
    }, []);
    //중복 로직 종료
    //중복 로직 종료
    //중복 로직 종료
    
    //로딩 중일 경우 로딩 바 표시
    if (!cypherList) return <Spinner />;
    
    //props와 cypherList를 합친 후 전달된 Component에 같이 전달
    const combinedProps = {
      ...props,
      cypherList
    };
    return <WrappedComponent {...combinedProps} />;
  };
  return Component;
};

export default withCypherList;

끝났다.

 

이제 사용하는 방법을 알아보자.

 

HOC 사용해보기

const CypherList= ({cypherList})=>{
  //props인 cypherList는 withCypherList로 부터 넘겨받은 데이터
  return (<div>
    {
      cypherList.map(cypher=>{
        return <p>{cypher.name}</p>
      })
    }
  </div>)
};

export default withCypherList(CypherList);

별로 어려울 것 없다.

 

그냥 export default CypherList에서 CypherList를 withCypherList로 감싸 주면 

withCypherList가 알아서 cypherList를 props로써 패치해준다.

 

이제 캐릭터 리스트가 필요한 컴포넌트는 같은 useState/useEffect로 떡칠하지 않고

withCypherList()로 감싸기만 하면 데이터를 얻어올 수 있다.

주의사항: React Hook "useState/useEffect" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function

const withCypherList= (WrappedComponent)=> (props)=> {
  const [state, setState]= useState();
  useEffect(()=>{}, []);
  
  return <WrappedComponent {...props}/>;
}

만약 다음과 같이 hoc를 생성했다면 위 에러를 만날 수 있을 것이다.

 

오류 내용에서 말했듯, hook은 callback 내부에서 사용할 수 없다.

 

따라서 약간의 편법으로 jsx 컴파일러를 속여야 한다.

const withCypherList = WrappedComponent => {
  const Component = props => {
    return <WrappedComponent {...props} />;
  };
  return Component;
};

export default withCypherList;

이런 형태로 작성함으로써 Component 함수 내부에서 useEffect/useState를 사용할 수 있게 된다. 

'FrontEnd > React' 카테고리의 다른 글

[React] package.json으로부터 version 가져오기  (0) 2020.11.05
[React] 에러 경계 (Error Boundary)  (0) 2020.11.04
[React] 404 페이지 만들기  (0) 2020.10.20
[React] MobX 실제로 사용해보기  (5) 2020.10.10
[React] MobX 개요  (0) 2020.10.04


Comments