React Hooks๐Ÿ’‹

Jung taeWoongยท2021๋…„ 5์›” 23์ผ
1

React.js

๋ชฉ๋ก ๋ณด๊ธฐ
13/19
post-thumbnail

React Hooks

ํ›…(Hook)์€ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์— ๋ฆฌ์•กํŠธ์˜ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ๋“ค์„ ๊ฐˆ๊ณ ๋ฆฌ์ฒ˜๋Ÿผ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ๊ฒƒ

  • Hook์€ React v16.8๋ถ€ํ„ฐ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ
  • ๋ฆฌ์•กํŠธ์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณต๋˜๋Š” Hook๋“ค์€ use๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ•จ์ˆ˜๋“ค
  • Hook์€ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ์—‘ํŠธ ๋ Œ๋”๋ง ๊ณผ์ •์—์„œ ํ•ญ์ƒ ๊ฐ™์€ ์ˆœ์„œ๋กœ ํ˜ธ์ถœ๋˜๋Š” ๊ทœ์น™์„ ์ด์šฉํ•˜์—ฌ ํ˜ธ์ถœ์ˆœ์„œ๋ฅผ ์ด์šฉํ•˜์—ฌ ์ƒํƒœ ๋งค์นญ

ํ›…(Hook)์˜ ๊ทœ์น™

  • React ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ๋งŒ ์‚ฌ์šฉ
  • ์ปดํฌ๋„ŒํŠธ ์•ˆ์˜ ๋ฐ˜๋ณต๋ฌธ, ์กฐ๊ฑด๋ฌธ, ์ค‘์ฒฉ๋œ ํ•จ์ˆ˜ ์•ˆ์—์„œ ํ›… ์‚ฌ์šฉ โŒ

useState

React์˜ useState()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—๋„ state๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • state์™€ state๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์Œ์œผ๋กœ ์ œ๊ณต
  • useState()๋Š” ์ „๋‹ฌ ๋ฐ›๋Š” ์ธ์ž๋กœ state์˜ ์ดˆ๊ธฐ ๊ฐ’์„ ์„ค์ • (์ฒซ ๋ Œ๋”๋ง ๋  ๋•Œ ๋”ฑ ํ•œ๋ฒˆ ์‚ฌ์šฉ)
const [state, setState] = React.useState(์ดˆ๊ธฐ๊ฐ’);

ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์˜ this.state์™€ ์ฐจ์ด์ 

useState()๋กœ ์„ค์ •๋œ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์˜ state๋Š” ํด๋ž˜์Šค์˜ state์™€ ๋‹ฌ๋ฆฌ 1๊ฐœ ์ด์ƒ์˜ state ๋งˆ๋‹ค ๊ฐœ๋ณ„์  ์—…๋ฐ์ดํŠธ๊ฐ€ ์š”๊ตฌ๋œ๋‹ค.
๋ฐ˜๋ฉด ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ๋Š” state ์—…๋ฐ์ดํŠธ ์‹œ, state์˜ ์ผ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ต์ฒดํ•˜๊ณ  ํ•ฉ์นœ๋‹ค

useEffect

useEffect() ํ›…์€ ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์˜ ๋‹ค์Œ์˜ ๋ผ์ดํ”„ ์‚ฌ์ดํด ํ›…์„ ํ•˜๋‚˜์˜ API๋กœ ํ†ตํ•ฉํ•œ ๊ฒƒ

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

useEffect => use + sideEffect??
๋ผ์ดํ”„์‚ฌ์ดํด์€ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๊ณ 
์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ๊ณผ์ •์—์„œ ๊ตฌํ˜„๋  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ด๊ธฐ์—
์ด๋Ÿฌํ•œ ๋™์ž‘์„ Side Effect(๋ถ€์ž‘์šฉ)์ด๋ผ ๋ถ€๋ฅธ๋‹ค.

์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง, ์—…๋ฐ์ดํŠธ ์ดํ›„ ์‹œ์ 

์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ์„ค๋ช…
DOM ์ปจํŠธ๋กค์‹ค์ œ DOM ๋…ธ๋“œ์ธ ๋ฌธ์„œ ์ œ๋ชฉ ์š”์†Œ <title>์— ์ ‘๊ทผ ๋ฐ ์ฝ˜ํ…์ธ  ๋ณ€๊ฒฝ
Time ์ปจํŠธ๋กคํŠน์ • ์‹œ๊ฐ„ ์ดํ›„ <title> ์ฝ˜ํ…์ธ  ๋ณ€๊ฒฝ
import React, { useState, useEffect } from 'react';

function CountDown(props) {
  const [count, setCount] = useState(10);
  
  // ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ
  useEffect(() => {
    // ์‹ค์ œ DOM ๋…ธ๋“œ์— ์ ‘๊ทผ
    const docTtile = document.querySelector('title');
    // ๋ฌธ์„œ ์ œ๋ชฉ์„ ๋ฐฑ์—…
    let originDocTitleContent = docTitle.innerText;
    // ๋ฌธ์„œ ์ œ๋ชฉ(title) ๋ณ€๊ฒฝ
    docTitle.innerText = `์นด์šดํŠธ ๋‹ค์šด (${count})`;
    // 3์ดˆ ๋’ค, ๋ฌธ์„œ ์ œ๋ชฉ์„ ๋ณต๊ตฌ
    setTimeout(() => docTitle.innerText = originDocTitleContent;
  })
  	return (
      <button type="button" onClick={() => setCount(count - 1)}>
        count down ({count})
      </button>
  )
}

useEffect()๋Š” ์ „๋‹ฌ ๋ฐ›์€ ํ•จ์ˆ˜๋ฅผ DOM ์—…๋ฐ์ดํŠธ ์ดํ›„ ์‹œ์ ์— ์‹คํ–‰ํ•œ๋‹ค.
์„ค์ •๋œ ํ•จ์ˆ˜๋Š” ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ์œ„์น˜ํ•ด ์žˆ์–ด ์ปดํฌ๋„ŒํŠธ์˜ state, props์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง(componentDidMount), ์—…๋ฐ์ดํŠธ ์ดํ›„(componentDidUpdate)์‹œ์ ์— ๋น ์ง์—†์ด ์‹คํ–‰๋œ๋‹ค.

์ปดํฌ๋„ŒํŠธ ์ œ๊ฑฐ ์ด์ „ ์‹œ์ 

ํ•„์š”ํ•œ ๊ฒฝ์šฐ, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ œ๊ฑฐ ๋˜๊ธฐ ์ „(componentWillUnmount)์— ํŠน์ • ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ์„ค์ • ๊ฐ€๋Šฅ

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

function CountDown(props) {
  const [count, setCount] = useState(10);
  useEffect(() => {
    ...
    // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ œ๊ฑฐ ๋ ๋•Œ ์‹คํ–‰๋  ํ•จ์ˆ˜
    return () => {
      clearTimeout(timeoutID):
    }
  });
  return (...)
}

์ดํŽ™ํŠธ ํ•จ์ˆ˜ ์„ฑ๋Šฅ ์ด์Šˆ

๋งค๋ฒˆ ๋ Œ๋”๋ง, ์—…๋ฐ์ดํŠธ ๊ณผ์ •์—์„œ ์ดํŽ™ํŠธ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋  ๊ฒฝ์šฐ ์„ฑ๋Šฅ์— ์•…์˜ํ–ฅ์„ ๋ผ์นœ๋‹ค.
๋น„๊ต ๊ณผ์ •์„ ํ†ตํ•ด ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ฐœ์ƒํ•  ๋•Œ๋งŒ ์ดํŽ™ํŠธ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋œ๋‹ค๋ฉด ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
useEffect()์— ๋‘๋ฒˆ์งธ ์ธ์ž๋กœ ๋ฐฐ์—ด๊ฐ’ ์•ˆ์— ๋น„๊ตํ•  ๋ฐ์ดํ„ฐ(state,props)๋ฅผ ๋„ฃ๋Š”๋‹ค.
๋นˆ ๋ฐฐ์—ด์ผ ๊ฒฝ์šฐ componentDidMount๋งŒ ์‹คํ–‰ (์ฒซ ๋ Œ๋”๋ง ํ•œ๋ฒˆ)

useEffect(
  () => {...},
  [count] // count ๊ฐ’์ด ๋ณ€ํ•  ๋•Œ ๋งˆ๋‹ค ์‹คํ–‰๋จ
)

useCallback

ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์— ๋ฉค๋ฒ„๋ณ€์ˆ˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งŒ๋“ค์–ด์งˆ๋•Œ ํ•œ๋ฒˆ๋งŒ ๋งŒ๋“ค์–ด์ง€๊ณ  render()๋งŒ ๋ฐ˜๋ณต์ ์œผ๋กœ ํ˜ธ์ถœ๋˜๋Š” ๋ฐ˜๋ฉด์— ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๋Š” ํ•จ์ˆ˜๋ผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ณ€๊ฒฝ์ด๋˜๋ฉด ๋ธ”๋ก ์ „์ฒด๊ฐ€ ๊ณ„์† ๋ฐ˜๋ณตํ•ด์„œ ํ˜ธ์ถœ์ด ๋œ๋‹ค.
์ฆ‰, ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๋Š” props๋‚˜ state๊ฐ€ ๋ณ€๊ฒฝ์ด๋  ๋•Œ๋งˆ๋‹ค ํ•จ์ˆ˜ ์•ˆ์— ์žˆ๋Š” ๋ณ€์ˆ˜๋‚˜ ํ•จ์ˆ˜๋“ค์ด ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ๋Œ€์ฒด๋จ.

๋ฐ˜๋ฉด hooks๋“ค์€ ๋ฆฌ์•กํŠธ๊ฐ€ ์•Œ์•„์„œ ์ž๋™์œผ๋กœ ๊ธฐ์–ตํ•˜๋ฏ€๋กœ ์•„๋ฌด๋ฆฌ ๋งŽ์ด ํ˜ธ์ถœํ•ด๋„ ์—ฐ๊ฒฐ๋œ data๋Š” ๋”ฐ๋กœ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ๋˜์žˆ์–ด ๊ฐ’์„ ์žฌ์‚ฌ์šฉํ•œ๋‹ค.

props๋กœ ๋„˜๊ธฐ๋Š” ์ฝœ๋ฐฑํ•จ์ˆ˜๊ฐ€ ์žˆ์œผ๋ฉด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ๋Œ€์ฒด๋˜๋ฉด์„œ props์˜ ๋ณ€ํ™”๋กœ re-rendering๋˜๋Š” Side Effect(๋ถ€์ž‘์šฉ)์ด ์ƒ๊ธด๋‹ค.
์ด๋Ÿฐ ๋ถ€์ž‘์šฉ์„ ๋ฐฉ์ง€ํ•˜๊ณ ์ž ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ดuseCallback()

useRef

useRef()๋Š” ์‹ค์ œ DOM ๋…ธ๋“œ๋ฅผ ์ฐธ์กฐ(Ref.)ํ•  ๊ฒฝ์šฐ ์‚ฌ์šฉ
์ฐธ์กฐ ๋Œ€์ƒ์˜ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ .current ์†์„ฑ์„ ์‚ฌ์šฉ

import React, { useRef } from 'react'; // import hook

const buttonRef  = useRef(null); // useRef hook ์ƒ์„ฑ

<button ref={buttonRef}> // useRef ์„ค์ •

useRef()๋ฅผ ์‚ฌ์šฉํ•ด ์‹ค์ œ DOM ๋…ธ๋“œ๋ฅผ ์กฐ์ž‘ํ•œ ๊ฒฝ์šฐ, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‹ค์‹œ ๊ทธ๋ ค์ง€์ง€ ์•Š๋Š”๋‹ค.
๋ฌด์กฑ๊ฑด state ๋˜๋Š” props๊ฐ€ ๋ณ€๊ฒฝ์ด ๋˜์–ด์•ผ ์—…๋ฐ์ดํŠธ!!
๋งŒ์•ฝ ์‹ค์ œ DOM ๋…ธ๋“œ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•ด ํŠน์ • ์ฝ”๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด useCallback() ํ›…์„ ์‚ฌ์šฉ

/*
  useCallback() ํ›…์„ ์‚ฌ์šฉํ•ด ์ฐธ์กฐ ๋œ ์‹ค์ œ DOM ๋…ธ๋“œ์˜ width ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด
  ์ปดํฌ๋„ŒํŠธ state๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค์‹œ ๊ทธ๋ฆผ
*/
const [width, setWidth] = useState(0);
// ์‹ค์ œ DOM ๋…ธ๋“œ๋ฅผ ์ฝœ๋ฐฑ ์ฐธ์กฐ(Callback Ref.)ํ•˜์—ฌ ๋ณ€๊ฒฝ ๊ฐ์ง€์‹œ ์ปดํฌ๋„ŒํŠธ ๋ฆฌ๋ Œ๋”๋ง
const domPanelEl = useCallback((node) => {
  if (node !== null) {
    setWidth(node.getBoundingClientRect().width);
  }
}, []);

useContext

useContext()ํ›…์€ ์ปจํ…์ŠคํŠธ ๊ฐ์ฒด์˜ ํ˜„์žฌ ๊ฐ’(value)์„ ์†Œ๋น„(Consumer)ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•œ๋‹ค.

import React, { useContext } from 'react';
import AuthContext from '../context/AuthContext';

function SignIn(props) {
  const authContext = useContext(AuthContext);
  const { isAuth, signIn } = authContext;
  return (
    {
      isAuth ?
      	<div className="signed">๋กœ๊ทธ์ธ ๋จ</div> :
        <button type="button" onClick={() => signIn}>๋กœ๊ทธ์ธ</button>
    }
  )
}

useContext() ํ›…์€ ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์˜ static contextType ๋˜๋Š”
<Context.Consumer>์™€ ๋™์ผํ•˜๋ฉฐ, ์ปจํ…์ŠคํŠธ๋ฅผ ์ฝ๊ณ , ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๊ตฌ๋…ํ•˜๋Š” ๊ฒƒ๋งŒ ๊ฐ€๋Šฅ
์ปจํ…์ŠคํŠธ์˜ ๊ฐ’(value)์„ ๋ณ€๊ฒฝํ•˜๋ ค๋ฉด <Context.Provider>๊ฐ€ ํ•„์š”

Custome Hook

์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ์žฌ์‚ฌ์šฉ ๋  ์ˆ˜ ์žˆ๋Š” ๋กœ์ง๋“ค์„ ์ปค์Šคํ…€ ํ›…์œผ๋กœ ์ •์˜ํ•ด์„œ ์‚ฌ์šฉ

ํ›…์€ ๊ธฐ๋Šฅ์ด๋ผ๊ธฐ ๋ณด๋‹ค๋Š” ์ปจ๋ฒค์…˜(Convention, ํ˜‘์˜)์— ๊ฐ€๊น๋‹ค.
ํ›…์€ use๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ด๋ฆ„์„ ๊ฐ–๋Š” ์•ฝ์†๋œ ํ•จ์ˆ˜

// ์นด์šดํŠธ ๋‹ค์šด ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ปค์Šคํ…€ ํ›…
import React, { useState } from 'react';

// ์‚ฌ์šฉ์ž ์ •์˜ ํ›…
function useCountDownStatus(count) {
  const [countStatus, setCountStatus] = useState('์™„๋ฃŒ ์ „');
  if (count === 0) {
    setCountStatus('์™„๋ฃŒ');
  }
  return countStatus;
}

export default useCountDownStatus

์ •์˜ํ•œ ์ปค์Šคํ…€ ํ›…์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

import useCountDownStatus from '../hooks/useCountDownStatus';
const CountDownStatusMessage = ({ count }) => {
  const countStatus = useCountDownStatus(count);
  return countStatus === '์™„๋ฃŒ' ?
    <div>์™„๋ฃŒ ๋จ</div> :
    <div>์นด์šดํŠธ ๋‹ค์šด ์ค‘..</div>
}
profile
Front-End ๐Ÿ˜ฒ

0๊ฐœ์˜ ๋Œ“๊ธ€