[fastcampus] Ch.6-2 HOC (2)

productuidev·2022년 3월 10일
0

React Study

목록 보기
12/52
post-thumbnail
post-custom-banner

Hooks 배우기 전 HOC에 대한 이해 보충자료 정리


React Hooks와 비교해서 이해하는 HOC

  • React Hooks로 인해 얻는 이점들
  • 클래스형/함수형 컴포넌트 코드 비교

React Hooks은 무엇인가?

React Hooks는 ReactConf 2018에서 발표된 class없이
state를 사용할 수 있는 새로운 기능입니다.

React Hooks이 필요한 이유?

항상 기술은 그 전 것에 불편함이나 문제점을 해결하기 위해서 더욱 발전합니다. 그와 같이 React Hooks도 주로 Class Component로 사용되어온 React에서 느껴왔던 불편함이나 문제점들을 해결하기 위해서 개발되었습니다.
원래 React는 주로 Class Component를 사용하고 React Hooks는 Functional Component를 사용하기 때문에 그 부분부터 비교해보겠습니다.

Class/Functional Component의 차이

  • 둘다 브라우저에서 실행할 수 있도록 Babel로 컴파일하게 되는데, 컴파일 후 코드가 더 길어지는데..

Class Component

  • 더 많은 기능 제공
  • 더 긴 코드 양
  • 더 복잡한 코드
  • 더딘 성능
import React, { Component } from 'react'

export default class Hello extends Component {
  render() {
    return (
      <div>
        hello my friends!
      </div>
    )
  }
}

Functional component

  • 더 적은 기능 제공
  • 짧은 코드 양
  • 더 심플한 코드
  • 더 빠른 성능
import React from 'react'

export default function Hello() {
  return (
    <div>
      hello my friends!
    </div>
  )
}

함수형 컴포넌트에서 더 적은 기능을 제공한다는데 어떤 기능을 클래스 컴포넌트에 비해서 쓰지 못한다는 걸까?

리액트 생명주기

이렇게나 중요한 생명주기를 함수형 컴포넌트에서는 사용 못했기 때문에 함수형 컴포넌트가 더 간결하고 빠르더라도 클래스 컴포넌트를 써왔음

React 16.8 Hooks 업데이트

버전 업데이트 이후 함수형 컴포넌트에서도 생명주기를 사용할 수 있기에 데이터를 가져오고 컴포넌트 시작하자마자 API도 호출하고 많은 부분을 할 수 있게 되었음.
그럼 Hooks로 인해 어떻게 가능하게 되었는지 소스로도 확인

// Class

import React, { Component } from 'react'
import Axios from 'axios'

export default class Hello extends Component {

  construnctor(props) {
    super(props);
    this.state = { name: "" };
  }

  componentDidMount() {
    Axios.get('/api/user/name')
      .then(response => {
        this.setState({ name: response.data.name })
      })
  }

  render() {
    return (
      <div>
        My name is {this.state.name}
      </div>
    )
  }
}
// Functional

import React, { useEffect, useState } from 'react'
import Axios from 'axios'

export default function Hello() {
  const [Name, setName] = useState('')

  useEffect( () => {
    Axios.get('/api/user/name')
    .then(response => { setName(response.data.name) })
  }, [])

  return(
    <div>
      My Name is {Name}
    </div>
  )
}

Hooks으로 인한 또 다른 이점도 있나요?

// 일반 Class에서 생명주기를 사용하는 부분

componentDidMount() {
  // 컴포넌트가 마운트 되면 updateLists 함수를 호출
  this.updateLists(this.props.id)
}

componentDidUpdate(prevProps) {
  if (prevProps.id !== this.props.id) {
    // updateLists 함수를 호출할 때
    // 사용되는 id가 달라지면 다시 호출

    this.updateLists(this.props.id)
  }
}

// updateLists 함수 정의
updateLists = (id) => {
  fetchLists(id)
  .then( (lists) => this.setState({lists}) )
}
// React Hooks를 이용한 생명주기 이용 (Functional)

useEffect( () => {
  fetchLists(id)
  .then( (repos) => {setRepos(repos)} )
  }, [id]
)

Class에서 생명주기를 사용해서 작성한 코드와
Hooks을 이용해 Functional로 생명주기를 이용해 작성한 코드를 비교하면
Hooks 사용했을 때 코드가 더 간결해진 것을 확인할 수 있다.

그 이유는 Class Component에서는 생명주기를 이용할 때
componentDidMount와 ComponentDidUpdate, componentWillUnmount
이렇게 다르게 처리해주지만,
React Hooks를 사용할 때는
useEffect 안에서 다 처리를 해줄 수 있기 때문이다.

HOC(Higher Order Component)란?

HOC란 화면에서 재사용 가능한 로직만을 분리해서 component로 만들고, 재사용 불가능한 UI와 같은 다른 부분들은 parameter로 받아서 처리하는 방법입니다.

// A 페이지

export default class Apage extends Component {
  state = { user: [] }

  componentDidMount() {
    fetchUsers()
    .then(users => { this.setState({users}) })
  }

  render() {
    const {users} = this.state;
    return (
      <div>
        A 페이지
        {user.map( ({name, url}) => (
            <div key={name}>
              <p>{name}, {url}</p>
            </div>
          )
        )}
      </div>
    )
  }
}
// B 페이지
export default class Bpage extends Component {
  state = { user: [] }

  componentDidMount() {
    fetchUsers()
    .then(users => { this.setState({users}) })
  }

  render() {
    const {users} = this.state;
    return (
      <div>
        B 페이지
        {user.map( ({name, url}) => (
            <div key={name}>
              <p>{name}, {url}</p>
            </div>
          )
        )}
      </div>
    )
  }
}

DB나 API를 통해서 유저의 리스트를 가져온 후
A페이지와 B페이지, ... 등 모든 페이지에서도
데이터를 뿌려주고 싶을 때 중복되지 않게 코드 작성한다면..

function usersHOC(Component) {
  return class usersHOC extends React.Component {
    state = { user: [] }

    componentDidMount() {
      fetchUsers()
      .then( users => { this.setState({users}) } )
    }

    render() {
      return (
        <Component
          {...this.props}
          {...this.state}
        />
      )
    }
  }
}
function Apage ({users}) {
  // wrapped
}

export default userHOC(Apage)

function Bpage ({users}) {
  // wrapped
}

export default usersHOC(Bpage)

=> 유저 리스트를 가져오는 공통적인 부분은 HOC 컴포넌트에 넣어주고
그 HOC 컴포넌트로 각각의 컴포넌트를 감싸주면 모든 컴포넌트에 따로
인증을 위한 부분은 넣어주지 않아도 됩니다. Hooks가 나오기 전에는 이 방법이 추천되는 방법이었습니다.

하지만 여기서도 문제가 있습니다.
그 문제는 바로 너무나 많은 Wrapper 컴포넌트가 생길 수 있다는 겁니다. 아래에 그림처럼 Wrapper가 너무 많아지면 데이터의 흐름을 파악하기가 어려워 집니다. (렌더링된 DOM 확인 시 중첩된 코드)

<LanguageHOC>
  <ThemeHOC>
    <AuthHOC>
      <Apage />
    </AuthHOC>
  </ThemeHOC>
</LanguageHOC>

Custom React Hooks를 이용해 해결하기

function useAuth() {
  const [users, setUsers] = useState( [] );

  useEffect( () => {
    fetchUsers()
    .then( users => { setUsers(users); });
  }, [] );

  return [users];
}

function Apage() {
  const [users] = useAuth(); // hooks

  return (
    <div>
      A 페이지
      {
        users.map( ({name, url}) => (
            <div key={name}>
              <p>{name}, {url}</p>
            </div>
          )
        )
      }
    </div>
  );
}
profile
필요한 내용을 공부하고 저장합니다.
post-custom-banner

0개의 댓글