> For the complete documentation index, see [llms.txt](https://shhn0509.gitbook.io/react/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://shhn0509.gitbook.io/react/react-study/react/higher-order-component/practice.md).

# 고차 컴포넌트 예제

{% hint style="info" %}
이전에 공부한 [컨텍스트 예제](https://shhn0509.gitbook.io/react/react-study/react/context/context-practice)와 이어지는 예제 입니다.&#x20;
{% endhint %}

{% embed url="<https://stackblitz.com/edit/react-9vpzyc?file=src%2Fcontexts%2FMessage.js>" %}

## 1. 커스텀 래퍼 컴포넌트&#x20;

아래 return 되는 React 요소를 감싸주는 `<MessageContext.Provider />` 컴포넌트를 커스텀 컴포넌트로 만들 수 있다.&#x20;

```javascript
return (
  <MessageContext.Provider
    value={{
      message: this.state.message,
      changeMessage: this.changeMessage
    }}
  >
    <Print>{this.state.message}</Print>
    <GrandParent parentCount={2} childrenCount={2} />
  </MessageContext.Provider>
);
```

아래와 같이 컴포넌트를 반환하는 함수를 만든다. 아래의 함수에서 `props`는 `<MessageContext.Provider />`의 `value`와 `children`을 받아오게 된다. 그렇기 때문에 `props`를 받아오고 해당 `props`를 번거롭게 다 지정해서 컴포넌트의 속성으로 구조 분해 할당하는 것이 아니라 나머지 매개 변수를 사용하면 간단하다.&#x20;

```javascript
// 컨텍스트의 Provider(공급자) 컴포넌트를 래퍼 컴포넌트로 내보내기
export const MessageProvider = props => {
  return <MessageContext.Provider {...props} />;
};
```

![커스텀 래퍼 컴포넌트 적용 하기 ](/files/-MTia7NpwFXcjdCsbpY7)

#### 1.2 Provider 래퍼 컴포넌트&#x20;

Firebase 인증 예제에서 사용된 코드이다. `value`의 객체 형태로 작성하지 않고 `callback` 함수 같은 경우엔 아래와 같이 `useCallback()` 훅을 사용해서 함수를 내보낼 수 있다.&#x20;

{% hint style="info" %}
`useCallback()` 훅은 자식 컴포넌트에 함수를 내보낼 때 재조정을 방지하기 위해 사용 되는 훅이다.&#x20;
{% endhint %}

```javascript
const FirebaseAuthProvider = ({ children, ...restProps }) => {
  const [state, dispatch] = React.useReducer(
    firebaseAuthReducer,
    initialAuthValue
  )

  const signUp = React.useCallback((email, password) => {
    return auth
      .createUserWithEmailAndPassword(email, password)
      .then((response) => {
        console.log(response.user)
        console.log('회원가입 성공')
        dispatch(signUpAction(response.user))
      })
  }, [])

  const signIn = React.useCallback((email, password) => {
    return auth.signInWithEmailAndPassword(email, password).then((response) => {
      console.log(response.user)
      console.log('로그인 성공')
      dispatch(signInAction(response.user))
    })
  }, [])

  const signOut = React.useCallback(() => {
    return auth.signOut().then(() => {
      console.log('로그아웃 성공')
      dispatch(signOutAction())
    })
  }, [])

  const value = { ...state, signUp, signIn, signOut }

  return (
    <FirebaseAuthContext.Provider value={value} {...restProps}>
      {children}
    </FirebaseAuthContext.Provider>
  )
}
```

## 2. HOC → 일반 함수&#x20;

먼저 생각하기, 고차 컴포넌트를 왜 사용할까?&#x20;

context를 사용해서 자식 컴포넌트가 value를 받아오려면 Consumer 컴포넌트를 사용하거나 context Type을 사용해서 value를 받아와야 한다.&#x20;

Cunsumer을 사용해서 value를 받아오는 것은 코드가 복잡해져 개인적으로 잘 사용하지 않게 된다.&#x20;

그리고 (클래스 컴포넌트의) context Type을 사용해서 value를 받아오는 방식은 클래스 컴포넌트를 사용해야 한다는 제약이 있어서 번거롭다.&#x20;

그래서 context Type 방식을 사용할 때 클래스 컴포넌트를 매번 사용해야 한다는 번거로움을 해결하기 위해 함수형 컴포넌트를 전달했을 때 context Type을 사용하는 클래스 컴포넌트가 반환되는 고차 컴포넌트를 만들어 사용한다.&#x20;

{% hint style="info" %}
**순수 함수란?**&#x20;

영어로는 pure function이라고 하며 한가지의 기능을 하는 함수를 말한다. 예를들어 컴포넌트를 전달하여 컴포넌트를 반환하더나 숫자를 전달하여 숫자를 반환하는 함수를 말한다.&#x20;

만약 문자열을 전달했는데 숫자가 나온다? 그럼 그것 은 순수 함수라고 말할 수 없다.&#x20;

쉽게 생각하면 믹서기에 사과를 넣었으면 사과즙이 나와야 하는 것이 정상이다. 당근즙이 나온다면 얼마나 이상한가?&#x20;

이처럼 한 가지 일을 하는 함수를 순수 함수라고 한다.&#x20;
{% endhint %}

![고차 컴포넌트 이해를 돕기 위한 이미지 ](/files/-MTipZ7K34ipOzfPqStN)

### 2.2 컴포넌트 별칭 등록&#x20;

displayName 속성을 사용해서 컴포넌트의 이름을 설정할 수 있다. 이렇게 설정하면 디버깅에 용이하다.&#x20;

```javascript
_withMessage.displayName = "whidMessage";
```

![](/files/-MTirns8m3Zy4KGbI3v9)

{% embed url="<https://stackblitz.com/edit/react-mdvayi?file=src%2Fcontexts%2FMessage.js>" %}

## 3. HOC → 커링 함수&#x20;

{% embed url="<https://stackblitz.com/edit/react-k9ithv?file=src%2Fcomponents%2FChild%2Findex.js>" %}

## 4. 커스텀 훅(Hook)

속성 병합 이전에 배웠다 시피 속성의 이름이 같은 속성을 부모로 부터 전달 받으면 자식의 속성은 무시된다고 배웠다.&#x20;

그 때, 나머지 연산자를 사용해서 병합하는 방법을 배웠다. 이번에는 따로 조건처리를 해서 오류가 나는 것을 방지하는 방법으로 사용해보자.&#x20;

![](/files/-MTj4bNHaDNTn-YSzjmb)

&#x20;아래 handle이라는 변수에 context의 메서드와 부모 컴포넌트에서 받아온 onClick 속성을 병합했다. 이렇게되면 이전에 속성이 무시되지 않고 병합되어 정상 작동한다.&#x20;

```javascript
// Child Component 
const handleClick = () => {
  context?.changeMessage(message);
  typeof onClick === "function" && onClick();
};
```

{% embed url="<https://stackblitz.com/edit/react-ekiziu?file=src%2Fcontexts%2FMessage.js>" %}

## 5. 재조정 주의하기&#x20;

아래 처럼 defaultStyle을 컴포넌트 내부에 생성할 경우 재조정의 문제로 브라우저의 성능에 문제가 될 수 있다.&#x20;

![컴포넌트 내부에 defaultStyle 객체를 정의  ](/files/-MTj70QpXLQDX3lGfckV)

아래와 같이 컴포넌트 외부에 defaultStyle를 정의할 경우 전역이 오염된다.&#x20;

![](/files/-MTj7PfP2-kAqHnKiV28)

가장 좋은 방법은 아래와 같이 네임스페이스를 사용해서 컴포넌트 외부에 객체를 정의 하는 방법이다.

이렇게 하면 전역을 오염시키지 않고 재조정 문제도 해결할 수 있어서 좋다.&#x20;

![](/files/-MTj7tL8666qMFw_B5bO)

## 6. props 검사하기&#x20;

오타로 인해 속성과 context를 받아오질 못했다. 그래서 속성을 잘 받아 왔는지 확인하기 위해 props를 검사해 본다.&#x20;

* class에서 context는 기본적으로 object로 받아온다.&#x20;
* 함수에서 context는 기본적으로 array로 받아온다.&#x20;

![](/files/-MTjVVHIogjZfA3XlGfZ)

## 7. 기억하기&#x20;

매개 변수 안에서 구조 분해 할당&#x20;
