기은P
시간이 멈추는 장소
기은P
  • Programming (272)
    • 개발노트 (1)
    • FrontEnd (56)
      • ES&JS 문법 (14)
      • HTML&CSS (4)
      • React 기본 (18)
      • React 심화 (12)
      • React 이슈 (2)
      • Project 연습 (1)
      • Next.js (5)
    • Backend&Devops (33)
      • AWS (2)
      • Docker (9)
      • Jenkins (6)
      • Nginx (6)
      • Node.js (1)
      • ElasticSearch (5)
      • 프레임워크&아키텍처 (2)
      • 암호화 (0)
      • 기타 (2)
    • 알고리즘 (3)
    • C# (8)
      • WPF (8)
    • Java (51)
      • 순수 Java (18)
      • RDF&Jena (12)
      • RCP&GEF (9)
      • JMX (5)
      • JMapper (3)
      • 오류해결 (4)
    • Database (21)
      • RDBMS (9)
      • NoSQL (2)
      • TSDB (1)
      • GraphQL (1)
      • Hibernate (3)
      • 데이터베이스 이론 (4)
      • Redis (1)
    • 프로토콜 (11)
      • Netty (4)
      • gRPC (5)
      • 프로토콜 개념 (2)
    • Server (4)
      • Linux (4)
    • 2020 정보처리기사 필기 (43)
      • 목차 (1)
      • 기출문제 (1)
      • 1과목 - 소프트웨어 설계 (6)
      • 2과목 - 소프트웨어 개발 (7)
      • 3과목 - 데이터베이스 구축 (8)
      • 4과목 - 프로그래밍 언어 활용 (7)
      • 5과목 - 정보시스템 구축 관리 (10)
    • 2020 정보처리기사 실기 (31)
      • 목차 (4)
      • 기출예상문제 (19)
      • 실기요약 (8)
    • 빅데이터분석기사 필기 (4)
      • 목차 (0)
      • 필기 요약 (3)
    • 전기 공학 (1)
      • CIM (1)
    • 산업자동화시스템 (3)
      • SCADA (1)
      • OPC UA (2)
    • 디자인패턴 (1)
    • 휴지통 (0)

공지사항

  • 공지사항/포스팅 예정 항목

최근 댓글

최근 글

전체 방문자
오늘
어제

티스토리

hELLO · Designed By 정상우.
기은P

시간이 멈추는 장소

FrontEnd/React 심화

[React] HOC(고차 컴포넌트) 함수형 예제

2022. 11. 15. 17:18
반응형

1. 개요

기술면접을 보았을때 HOC란 무엇인가?라고 물었을때 잘 모른다고 대답을 했었는데요, 살짝 찾아보니까 메인 다큐먼트에서는 클래스형 컴포넌트에서 사용되는 것이 있었습니다.

기술면접에서 물어볼 정도면 뭔가 중요한 내용이 있는게 아닐까 싶어서 HOC를 함수형으로 사용할 수 있는 방법을 찾아보았고, 어째서 HOC에서 물어보셨는지에 대해 깨닳게 되었습니다!

 

예제로 사용한 깃허브 주소입니다.

https://github.com/kimdongjang/hoc_test

 

GitHub - kimdongjang/hoc_test

Contribute to kimdongjang/hoc_test development by creating an account on GitHub.

github.com

 

 

2. HOC(Higher Order Component)

고차 컴포넌트는 컴포넌트를 매개 변수로 받아 새로운 컴포넌트로 반환하는 함수를 의미합니다.

React에서 저희는 함수형 컴포넌트를 작성합니다.

HOC는 이렇게 작성한 컴포넌트를 인자로 받아 Wrapping 해서 새로운 컴포넌트를 반환해 컴포넌트의 재사용성을 늘리는 역할을 해줍니다.

 

 

3. 사용방법

HOC는 with로 시작하는 컨벤션으로 파일을 작성합니다.

먼저 예제로 작성할 코드를 보면서 설명을 적겠습니다.

 

HOC를 사용해서 여러 개의 이미지마다 공통적으로 마우스를 hover 할 때 반응하는 컴포넌트를 만들으려고 합니다.

1차적으로 작성된 코드가 Image만 있는 컴포넌트이고, 2차적으로 Image마다 event를 추가한다고 했을 때, HOC를 사용해 이 Image들에게 Wrapping을 하면 Image마다 event를 작성할 필요없이 Wrap하는 것으로 코드 작성이 쉬워집니다.

HOC가 재사용성을 늘리기 위해 사용하는 것이기 때문에 이러한 관점에서 코드를 보는 것이 중요합니다.

 

exam1/ImageBox.jsx

import withHover from "./withHover";

function ImageBox({
  imageUrl,
  imageTitle,
  isHovered,
  handleMouseEnter,
  handleMouseLeave
}) {
  return (
    <div>
      {isHovered && <div id="hover">{imageTitle}</div>}
      <img
        src={imageUrl}
        alt={imageTitle}
        width="400px"
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
      />
      <h5>이미지에 마우스를 올리면 이미지 제목이 표시됩니다.</h5>
    </div>
  );
}

export default withHover(ImageBox);

 

exam1/withHover.jsx

import { useState } from "react";

export default function withHover(InnerComponent) {
  // props는 App.jsx 부모에서 prop로 전달한 데이터
  return (props) => {
    const [isHovered, setIsHovered] = useState(false);

    function handleMouseEnter() {
      setIsHovered(true);
    }

    function handleMouseLeave() {
      setIsHovered(false);
    }

    return (
      <InnerComponent
        {...{
          ...props,
          handleMouseEnter,
          handleMouseLeave,
          isHovered
        }}
      />
    );
  };
}

위와 같은 코드가 HOC 컴포넌트이고, 인자로 Wrap할 컴포넌트를 받아서 return값으로 컴포넌트를 반환합니다!

 

유의할 점은 함수형으로 작성할 경우에는 위처럼

function withHOC() 내부에

return () => {

   return (<innerComponent/>)

}

와 같이 return을 2개를 작성해서 사용해야 한다는 점입니다.

 

 

App.js

import logo from './logo.svg';
import './App.css';
import ImageBox from './exam1/ImageBox';
import { useEffect, useState } from 'react';
import axios from 'axios';
import TodoList from './exam2/TodoList';
import UserList from './exam2/UserList';

function App() {
  const url = "https://dog.ceo/api/breeds/image/random";
  const [title, setTitle] = useState('')
  const [image, setImage] = useState('');
  useEffect(() => {
    axios.get(url).then(response => { setTitle(response.data.message); setImage(response.data.message) })
  }, [])
  return (
    <div className="App">
      <ImageBox imageUrl={image} imageTitle={title} />
      <UserList />
      <TodoList />
    </div>
  );
}

export default App;

App.js에서는 랜덤 이미지를 호출해와 ImageBox에 이미지 주소와 title을 전달해줍니다.

*userList와 todoList는 주석을 달아서 테스트하면 됩니다.

 

 

 

4. 다른 코드

exam2/TodoList.jsx

import React, { useEffect, useState } from 'react';
import withHOC from './withHoc';

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>
  );
}

export default withHOC(TodoLIst, 'todos');

 

exam2/UserList.jsx

import React, { useEffect, useState } from 'react';
import withHOC from './withHoc';

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>
  );
}

export default withHOC(UserList, 'users');

 

exam2/withHoc.jsx

import React, { useEffect, useState } from 'react';

function withHOC(WrappedComponent, entityTitle) {
  return function () {
    const [data, setData] = useState([]);
    const [term, setTerm] = useState('');

    useEffect(() => {
      const timer = setTimeout(() => {
        let fetchData =
          entityTitle === '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 (entityTitle === 'users') {
        const { name } = d;
        return name.indexOf(term) >= 0;
      }

      if (entityTitle === 'todos') {
        const { title } = d;
        return title.indexOf(term) >= 0;
      }
    });

    return (
      <div>
        <h2>{entityTitle}</h2>
        <input
          type="text"
          value={term}
          onChange={(e) => setTerm(e.target.value)}
        />
        <WrappedComponent data={filterData}></WrappedComponent>
      </div>
    );
  };
}

export default withHOC;

 

App.js

위에 있는 App.js 그대로 사용하면 됩니다.

 

 

 

참고

https://ostarblog.tistory.com/12

https://velog.io/@ellie12/React-HOC-%EA%B3%A0%EC%B0%A8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8

반응형
저작자표시 변경금지 (새창열림)

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

[React] useMemo와 useCallback의 차이  (5) 2022.11.03
[React] 렌더링(리렌더링)  (0) 2022.11.03
[React] SEO 처리 하기  (0) 2022.10.19
[React] SSR과 CSR(SPA) 차이  (0) 2022.10.19
[React] Redux Toolkit 에서의 비동기 처리(createAsyncThunk, extraReducers)  (0) 2022.09.19
    'FrontEnd/React 심화' 카테고리의 다른 글
    • [React] useMemo와 useCallback의 차이
    • [React] 렌더링(리렌더링)
    • [React] SEO 처리 하기
    • [React] SSR과 CSR(SPA) 차이
    기은P
    기은P
    기은P의 블로그 일상과 개발 관련 포스팅 #React #Typescript #Next #Nest https://github.com/kimdongjang

    티스토리툴바