PROGRAMMING/React

React :: Hook, useState, useEffect, ์‚ฌ์šฉ์ž ์ •์˜ Hook

\b\t 2021. 3. 31. 13:09

Hook

Hook์€ ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์—์„œ React state์™€ ์ƒ๋ช…์ฃผ๊ธฐ ๊ธฐ๋Šฅ(lifecycle features)์„ “์—ฐ๋™(hook into)“ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜๋ฅผ ๋งํ•œ๋‹ค.

useState, useEffect ์™€ ๊ฐ™์€ ๋‚ด์žฅ Hook ์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์ง์ ‘ ์ •์˜ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

 

Hook ์‚ฌ์šฉ ๊ทœ์น™

Hook์€ ๊ทธ๋ƒฅ JavaScript ํ•จ์ˆ˜์ด์ง€๋งŒ, ๋‘ ๊ฐ€์ง€ ๊ทœ์น™์„ ์ค€์ˆ˜ํ•ด์•ผ ํ•œ๋‹ค.

  • **์ตœ์ƒ์œ„(at the top level)**์—์„œ๋งŒ Hook์„ ํ˜ธ์ถœ! ๋ฐ˜๋ณต๋ฌธ, ์กฐ๊ฑด๋ฌธ, ์ค‘์ฒฉ๋œ ํ•จ์ˆ˜ ๋‚ด์—์„œ Hook์„ ์‹คํ–‰ํ•˜๋ฉด ์•ˆ๋จ
  • React ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ๋งŒ Hook์„ ํ˜ธ์ถœ! (๋‹น์—ฐํžˆ, Class ์•ˆ์—์„œ๋Š” Hook ์€ ๋™์ž‘ํ•˜์ง€ ์•Š์Œ) ์ผ๋ฐ˜ JavaScript ํ•จ์ˆ˜์—์„œ๋Š” Hook์„ ํ˜ธ์ถœํ•˜๋ฉด ์•ˆ๋จ

๊ทธ๋Ÿผ ๋ณธ๊ฒฉ์ ์œผ๋กœ ๋Œ€ํ‘œ์ ์ธ ๋‚ด์žฅ Hook ๋‘ ๊ฐœ๋ฅผ ์‚ดํŽด๋ณด์ž.


useState

useState ๋Š” ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” state ์˜ ๊ฐœ๋…์„ ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๋จผ์ €, ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์˜ ํ˜•ํƒœ๋ฅผ ์งš๊ณ  ๋„˜์–ด๊ฐ€๋ณด์ž.

 

ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ๋Š” ํฌ๊ฒŒ ๋‹ค์Œ์˜ ๋‘ ํ˜•ํƒœ๋กœ ์กด์žฌํ•œ๋‹ค.

function Example(props) {
  // ์—ฌ๊ธฐ์„œ Hook์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค
  return <div />;
}
const Example = (props) => {
  // ์—ฌ๊ธฐ์„œ Hook์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค
  return <div />;
}

 

๊ทธ๋ฆฌ๊ณ , ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์—์„œ state ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ์‚ฌ์šฉ๋˜์—ˆ๋‹ค.

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
  render() {
  	...
  }
}

 

์ด์ „์— Props, State ๋ฅผ ๋ฐฐ์šธ ๋•Œ State ๋Š” ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ–ˆ๋‹ค.

๊ทธ๋Ÿผ ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” State ์˜ ๊ฐœ๋…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š”๊ฐ€์— ๋Œ€ํ•œ ์˜๋ฌธ์„ ๊ฐ€์กŒ์—ˆ๋Š”๋ฐ, 

useState ๋ฅผ ์‚ฌ์šฉํ•ด์„œ State ์˜ ๊ฐœ๋…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

useState ์‚ฌ์šฉ ํ๋ฆ„ (์š”์•ฝ)

useState ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ํฌ๊ฒŒ ๋‹ค์Œ์˜ ์„ธ ๋‹จ๊ณ„๋กœ ์ด๋ฃจ์–ด์ง„๋‹ค.

 

1) useState ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด,  useState Hook ๋ฅผ React ์—์„œ ๊ฐ€์ ธ์˜จ๋‹ค.

  import React, { useState } from 'react';

2) useState ๋กœ state ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค.

const [count, setCount] = useState(0);

(์—ฌ๊ธฐ์„œ ๋Œ€๊ด„ํ˜ธ๊ฐ€ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์€ "๋ฐฐ์—ด ๊ตฌ์กฐ ๋ถ„ํ•ด"๋ผ๋Š” JavaScript ๋ฌธ๋ฒ•์ด๋‹ค)

 

3) useState ๊ฐ€ ๋งŒ๋“ค์–ด์ค€ state ๋ฅผ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋กœ state ๋ณ€์ˆ˜๋ฅผ ๊ฐฑ์‹ ํ•œ๋‹ค.

React ๋Š” ๋ณ€ํ™”๋ฅผ ์ธ์ง€ํ•˜๊ณ , ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฆฌ๋ Œ๋”๋งํ•œ๋‹ค.

import React, { useState } from 'react'; 2:
  function Example() {
    const [count, setCount] = useState(0); 5:
    return (
      <div>
        <p>You clicked {count} times</p>
        <button onClick={() => setCount(count + 1)}>10:         Click me
        </button>
      </div>
    );
  }

 

useState Hook ์„ ์‚ฌ์šฉํ•˜๋Š” ํ๋ฆ„์„ ์‚ดํŽด๋ดค๋‹ค.

๊ทธ๋Ÿฌ๋ฉด, useState ์— ์ธ์ž๋กœ ๋„˜๊ฒจ์ค€ ๊ฒƒ์€ ๋ฌด์—‡์œผ๋กœ ๋ฐ›์€ ๊ฒƒ์€ ๋ฌด์—‡์ผ๊นŒ?

๋จธ๋ฆฟ์†์— useState ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ๋ฆ„์„ ๊ทธ๋ ค๋‘๊ณ , ๋” ์„ธ๋ถ€์ ์œผ๋กœ ์‚ดํŽด๋ณด์ž.

 

useState ์„ธ๋ถ€ ์‚ฌํ•ญ

useState Hook ์„ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ํ˜ธ์ถœํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

(ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ๋Š” this ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ง์ ‘ useState ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์‚ฌ์šฉ)

  const [fruit, setFruit] = useState('apple');

์ด๊ฑด ๋ฌด์—‡์„ ์˜๋ฏธํ• ๊นŒ?

 

๋จผ์ € useState ์˜ ์ธ์ž๋กœ ๋„˜๊ฒจ์ค€ 'apple' ์€ fruit state ์˜ ์ดˆ๊ธฐ๊ฐ’์ด๋‹ค. 

๊ทธ๋ฆฌ๊ณ  useState ๋Š” 'apple' ์„ ์ดˆ๊ธฐ๊ฐ’์œผ๋กœ ๊ฐ€์ง€๋Š” fruit state ์™€ fruit ์˜ ๊ฐ’์„ ๊ฐฑ์‹ ํ•ด์ค„ ์ˆ˜ ์žˆ๋Š” setFruit ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.

 

์ฆ‰, useState ๋ฅผ ํ†ตํ•ด ์ € ๋‘ ๊ฐ’์„ ๋ฐ›์•„์™”๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

//state ๊ฐ’ ๋ณ€๊ฒฝํ•˜๊ธฐ
setFruit('banana')

//state ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ
 <p>You're fruit is {fruit} !</p>

์ด๋Ÿฌ๋ฉด fruit state ๊ฐ’์€ 'apple' ์ด ์•„๋‹Œ 'banana' ๋กœ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

 

์ด๊ฒŒ ๋งŒ์ผ ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์˜€๋‹ค๋ฉด, this.state.fruit ๋กœ ์ ‘๊ทผํ•ด์•ผ ํ•˜๊ณ  state ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ์—๋„ {this.state.fruit} ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ–ˆ์„ ๊ฒƒ์ด๋‹ค.

//state ๊ฐ’ ๋ณ€๊ฒฝํ•˜๊ธฐ
this.setState({fruit: 'banana'})

//state ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ
 <p>You're fruit is {this.state.fruit} !</p>

๋”์šฑ ๊ฐ„ํŽธํ•˜๊ณ  ์ง๊ด€์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ ๊ฒƒ์ด๋‹ค.

 

 

์ •๋ฆฌํ•˜์ž๋ฉด, useState ์˜ ์ธ์ž๋กœ ๋งŒ๋“ค state ์˜ ์ดˆ๊ธฐ๊ฐ’์„ ๋˜์ ธ์ฃผ๋ฉด, useState ๊ฐ€ state ์™€ state ๋ฅผ ๊ฐฑ์‹ ํ•  ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ด์ฃผ๋Š” ๊ฒƒ!

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์—ฌ๋Ÿฌ state ๋ณ€์ˆ˜๋ฅผ ์ •์˜ํ•ด๋‘๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);

useEffect

React class์˜ componentDidMount ๋‚˜ componentDidUpdate, componentWillUnmount์™€ ๊ฐ™์€ ๋ชฉ์ ์œผ๋กœ ์ œ๊ณต๋˜์ง€๋งŒ, ํ•˜๋‚˜์˜ API๋กœ ํ†ตํ•ฉ๋œ ๊ฒƒ์ด๋‹ค.

(์ฐธ๊ณ : 2021.03.21 - [PROGRAMMING/React] - React :: ์ƒ๋ช…์ฃผ๊ธฐ (Lifecycle))

 

์™œ ์ด๋“ค์„ ํ†ตํ•ฉํ–ˆ๊ณ , ๊ทธ๊ฒŒ ์žฅ์ ์ด ๋ ๊นŒ?

 

๋‹ค์Œ ๋‚ด์šฉ์„ ์ƒ๊ฐํ•ด๋ณด์ž.

 

๋ Œ๋”๋ง ์ดํ›„์— ํ•ญ์ƒ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ธธ ๋ฐ”๋ž€๋‹ค๋ฉด, ์–ด๋–ค ์ƒ๋ช… ์ฃผ๊ธฐ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ?

์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ด์ œ ๋ง‰ ๋งˆ์šดํŠธ ๋œ ๊ฒƒ์ธ์ง€, ์•„๋‹ˆ๋ฉด ์—…๋ฐ์ดํŠธ๋œ ๊ฒƒ์ธ์ง€์— ์ƒ๊ด€์—†์ด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•œ๋‹ค.

๊ทธ๋Ÿผ, ์ƒ๋ช… ์ฃผ๊ธฐ ํ•จ์ˆ˜๋ฅผ ์ƒ๊ฐํ–ˆ์„ ๋• componentDidMount์™€ componentDidUpdate ์„ ๋ชจ๋‘ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

๊ทธ๊ฒƒ๋„ ๊ฐ™์€ ์ฝ”๋“œ๋กœ ์‹คํ–‰ํ•ด์•ผํ•œ๋‹ค. ๋‹ค์Œ์ฒ˜๋Ÿผ..

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }  
  
  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

์ด๋Š” ์ƒ๋‹นํžˆ ๋น„ํšจ์œจ์ ์ธ ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค.

 

๋ฐ˜๋ฉด useEffect Hook ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋งค ๋ Œ๋”๋ง ์ดํ›„์— effects๋ฅผ ์‹คํ–‰ํ•œ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋ Œ๋”๋ง๋„ ํฌํ•จํ•ด์„œ!

(์šฐ๋ฆฌ๊ฐ€ React ์—๊ฒŒ ๋„˜๊ธด ์ด Hook ํ•จ์ˆ˜๋ฅผ 'effect' ๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.)

 

๊ทธ๋ž˜์„œ ๊ทธ๋ƒฅ ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 

* useEffect Hook ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋จผ์ € React ๋กœ๋ถ€ํ„ฐ ๋ฐ›์•„์™€์•ผ ํ•œ๋‹ค.

import React, { useState, useEffect } from 'react';
function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {    
    document.title = `You clicked ${count} times`;  
  });

์ค‘๋ณต๋œ ์ฝ”๋“œ๋„ ์—†๊ณ , ๊ฐ€๋…์„ฑ๋„ ๋Š˜์—ˆ๋‹ค.

 

useEffect ํŠน์ง•

  • ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ์‚ฌ์šฉํ•œ๋‹ค.
    -> effect ๋ฅผ ํ†ตํ•ด state ๋ณ€์ˆ˜์™€ ๊ฐ์ข… prop ์—๋„ ์ ‘๊ทผ ํ•  ์ˆ˜ ์žˆ๋„๋ก!
  • ๋ Œ๋”๋ง ์ดํ›„์— ๋งค๋ฒˆ ์ˆ˜ํ–‰๋œ๋‹ค.
    (๋งˆ์šดํŠธ, ์—…๋ฐ์ดํŠธ๋ผ๋Š” ๊ฐœ๋…๋ณด๋‹ค๋Š” ๋ Œ๋”๋ง ์ดํ›„์— ๋ฐœ์ƒ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.)
    React ๋Š” effect ๊ฐ€ ์ˆ˜ํ–‰๋˜๋Š” ์‹œ์ ์— ์ด๋ฏธ DOM ์ด ์—…๋ฐ์ดํŠธ ๋˜์—ˆ์Œ์„ ๋ณด์žฅํ•œ๋‹ค.
  • useState์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ effect๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Clean-Up

์ปดํฌ๋„ŒํŠธ๋ฅผ ์ •๋ฆฌ(Clean-Up) ์ด effect ์— ํ•„์š”ํ•  ๋•Œ๊ฐ€ ์žˆ๋‹ค.

๊ทธ๋Ÿด ๋• useEffect ์—์„œ ์ •๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ฉ๋‹ค.

 

effect ๊ฐ€ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ฆฌ์•กํŠธ๋Š” ๊ทธ ํ•จ์ˆ˜๋ฅผ ์ •๋ฆฌ๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์‹คํ–‰์‹œํ‚จ๋‹ค.

์ด๋Š” (๊ธฐ๋ณธ์ ์œผ๋กœ) ํ•œ ๋ฒˆ์ด ์•„๋‹ˆ๋ผ, ๋ Œ๋”๋ง์ด ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค, ๋งค๋ฒˆ ๋ฆฌ๋ Œ๋”๋ง ์‹œ์— ์ •๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค.

 

 

 

์ฝ”๋“œ๋ฅผ ๋ณด์ž.

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

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {    
    function handleStatusChange(status) {      
        setIsOnline(status.isOnline);    
      }    
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);    
    
    // effect ์ดํ›„์— ์–ด๋–ป๊ฒŒ ์ •๋ฆฌ(clean-up)ํ•  ๊ฒƒ์ธ์ง€ ํ‘œ์‹œํ•œ๋‹ค.    
    return function cleanup() {      
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);    
    };  
  });
  
  if (isOnline === null) {
    return 'Loading...';
  }
    
  return isOnline ? 'Online' : 'Offline';
}

์ด ์ฝ”๋“œ๋Š” ์นœ๊ตฌ์˜ ์˜จ๋ผ์ธ ์ƒํƒœ๋ฅผ ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ๋Š” ChatAPI ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•œ ์˜ˆ์ด๋‹ค.

์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒซ ๋งˆ์šดํŠธ ๋ ๋•Œ๋ฅผ ํฌํ•จํ•˜์—ฌ, ๋งค๋ฒˆ ๋ฆฌ๋ Œ๋”๋ง ํ•  ๋•Œ๋งˆ๋‹ค ์นœ๊ตฌ ๊ตฌ๋…-๊ตฌ๋… ํ•ด์ง€๋ฅผ ๋ฐ˜๋ณตํ•œ๋‹ค.

 

 

์ด๋Š” ๋ฒ„๊ทธ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์„ฑ๋Šฅ ์ €ํ•˜(๋งค๋ฒˆ ์ •๋ฆฌํ•˜๊ณ  ๋‹ค์‹œ ์‹คํ–‰)๊ฐ€ ์šฐ๋ ค๋  ์ˆ˜ ์žˆ๋‹ค.

์ด ์ ๋“ค์„ ์‚ดํŽด๋ณด์ž.

 

๋ฒ„๊ทธ ๋ฐฉ์ง€

์ด์ „์˜ ์ฝ”๋“œ๋กœ ์–ด๋–ป๊ฒŒ ๋ฒ„๊ทธ ๋ฐฉ์ง€๊ฐ€ ๋˜๋ƒ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ,

๋งŒ์ผ ํ”„๋กœ๊ทธ๋žจ์ด ๋Œ์•„๊ฐ€๋Š” ์ค‘์— (์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜์–ด์žˆ๋Š” ๋™์•ˆ) ์นœ๊ตฌ์˜ id, ์ฆ‰ friend.id ๊ฐ€ ๋ฐ”๋€Œ์—ˆ์œผ๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

๋‹ค๋ฅธ ์นœ๊ตฌ์˜ ์˜จ๋ผ์ธ ์ƒํƒœ๋ฅผ ๊ณ„์† ํ‘œ์‹œํ•˜๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค. ์ด๋Š” ๋ช…๋ฐฑํ•œ ๋ฒ„๊ทธ์ด๋‹ค.

 

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์˜€๋‹ค๋ฉด componentDidUpdate๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ์ง€๋งŒ,

useEffect ๋Š” ๋ฆฌ๋ Œ๋”๋ง ํ•  ๋•Œ๋งˆ๋‹ค , ๋‹ค์Œ์˜ effect ๋ฅผ ์ ์šฉํ•˜๊ธฐ ์ „์— ์ด์ „์˜ effect ๋Š” ์ •๋ฆฌํ•œ๋‹ค.

์ฆ‰ ์œ„์˜ ์ฝ”๋“œ์˜ ์‹คํ–‰ ๊ณผ์ •์„ ๊ฐ€์‹œํ™”ํ•ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

// { friend: { id: 100 } } state์„ ์‚ฌ์šฉํ•˜์—ฌ ๋งˆ์šดํŠธํ•œ๋‹ค.
ChatAPI.subscribeToFriendStatus(100, handleStatusChange);     // ์ฒซ๋ฒˆ์งธ effect๊ฐ€ ์ž‘๋™ํ•œ๋‹ค.

// { friend: { id: 200 } } state๋กœ ์—…๋ฐ์ดํŠธํ•œ๋‹ค..
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // ์ด์ „์˜ effect๋ฅผ ์ •๋ฆฌ(clean-up)ํ•œ๋‹ค.
ChatAPI.subscribeToFriendStatus(200, handleStatusChange);     // ๋‹ค์Œ effect๊ฐ€ ์ž‘๋™ํ•œ๋‹ค.

// { friend: { id: 300 } } state๋กœ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // ์ด์ „์˜ effect๋ฅผ ์ •๋ฆฌ(clean-up)ํ•œ๋‹ค.
ChatAPI.subscribeToFriendStatus(300, handleStatusChange);     // ๋‹ค์Œ effect๊ฐ€ ์ž‘๋™ํžŒ๋‹ค.

// ๋งˆ์šดํŠธ๋ฅผ ํ•ด์ œํ•œ๋‹ค.
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // ๋งˆ์ง€๋ง‰ effect๋ฅผ ์ •๋ฆฌ(clean-up)ํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์—์„œ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š”, ์—…๋ฐ์ดํŠธ ๋กœ์ง (componentDidUpdate) ์„ ๋นผ๋จน์œผ๋ฉด์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฒ„๊ทธ๋ฅผ ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ๋‹ค!

 

์„ฑ๋Šฅ ์ €ํ•˜ ๋ฐœ์ƒ์ด ์šฐ๋ ค๋œ๋‹ค๋ฉด...

์นœ๊ตฌ id ๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜์œผ๋ฉด ๊ตณ์ด ๊ตฌ๋… ํ•ด์ง€๋ฅผ ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

๊ทธ๋ž˜์„œ ์œ„์˜ useEffect ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜๋ฉด ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

useEffect(() => {
  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
  return () => {
    ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
  };
}, [props.friend.id]); // props.friend.id๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งŒ ์žฌ๊ตฌ๋…ํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ํŠน์ • ๊ฐ’๋“ค์ด ๋ฆฌ๋ Œ๋”๋ง ์‹œ์— ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋ฆฌ์•กํŠธ๋กœ ํ•˜์—ฌ๊ธˆ effect ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.

useEffect ์˜  ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ "์‹ ๊ฒฝ์“ฐ๋Š”" ๊ฒƒ์„ ๋„˜๊ฒจ์ฃผ๋ฉด ๋œ๋‹ค.

 

๋”ฐ๋ผ์„œ, ์œ„์˜ ์ฝ”๋“œ์—์„œ props.friend.id ๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด ๊ตฌ๋…-์žฌ๊ตฌ๋… ์€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. (ํ•ด๋‹น effect ์‹คํ–‰ x)

 

๊ทธ๋Ÿฐ๋ฐ, ์ด ์ตœ์ ํ™” ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐฐ์—ด์ด ์ปดํฌ๋„ŒํŠธ ๋ฒ”์œ„ ๋‚ด์—์„œ ๋ฐ”๋€Œ๋Š” ๊ฐ’ + effect ์— ์˜ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๊ฐ’์„ ๋ชจ๋‘ ํฌํ•จํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•ด์•ผ ํ•œ๋‹ค.

 

๋งŒ์ผ effect ๋ฅผ ๋งˆ์šดํŠธ์™€ ๋งˆ์šดํŠธ ํ•ด์ œ ์‹œ์— ๋”ฑ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, useEffect ์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ๋นˆ ๋ฐฐ์—ด([]) ์„ ๋„˜๊ฒจ์ฃผ๋ฉด ๋œ๋‹ค.

(์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์˜ componentDidMount์™€ componentWillUnmount ๋ชจ๋ธ์— ๋” ๊ฐ€๊น๋‹ค)

 

Multiple useEffect

์—ฌ๋Ÿฌ effect ๋ฅผ ์„ค์ •ํ•ด์„œ ๊ฐ effect ๊ฐ€ ํ•˜๋Š” ์ผ์„ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์—์„œ ์ƒ๋ช… ์ฃผ๊ธฐ API ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์—๋Š” '์‹œ๊ธฐ'์— ๋งž์ถฐ์„œ ๊ธฐ๋Šฅ์„ ๋‹ค ๋„ฃ์–ด์•ผ ํ–ˆ์ง€๋งŒ, useEffect ๋Š” '๊ธฐ๋Šฅ'์— ๋งž์ถฐ์„œ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ฆ‰, ๊ตฌ๋…์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์ œ๊ฑฐํ•˜๋Š” ๋กœ์ง๊ณผ ๊ฐ™์ด ์„œ๋กœ ๊ด€๋ จ ์žˆ๋Š” ์ฝ”๋“œ๋“ค์„ ํ•œ๊ตฐ๋ฐ์— ๋ชจ์•„์„œ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {    
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {    
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  // ...
}

์‚ฌ์šฉ์ž ์ •์˜ Hook

์‚ฌ์šฉ์ž ์ •์˜ Hook ์„ ๋งŒ๋“ค์–ด๋‘๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์ด๋ฆ„์ด use๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜์ด๋‹ค.
  • ๋‹ค๋ฅธ Hook์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์กฐ๊ฑด๋ถ€ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹ˆ์–ด์•ผ ํ•œ๋‹ค.
  • ๊ฐ™์€ Hook์„ ์‚ฌ์šฉํ•˜๋Š” ๋‘ ๊ฐœ์˜ ์ปดํฌ๋„ŒํŠธ๋Š” state๋ฅผ ๊ณต์œ ํ•˜์ง€ ์•Š๋Š”๋‹ค

์œ„์—์„œ ๊ณ„์† ์‚ดํŽด๋ณธ ์˜ˆ์ œ์—์„œ ์นœ๊ตฌ์˜ ์ƒํƒœ๋ฅผ ๊ตฌ๋…ํ•˜๊ณ , ์˜จ๋ผ์ธ ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ฝ”๋“œ๋Š” ๋‹ค๋ฅธ ๊ณณ์—์„œ๋„ ์“ฐ์ผ ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿผ ๊ทธ ๋ถ€๋ถ„์„ ์‚ฌ์šฉ์ž ์ •์˜ Hook ๋กœ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ์–ด๋–จ๊นŒ?

 

import { useState, useEffect } from 'react';

function useFriendStatus(friendID) {  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

 

์ด useFriendStatus Hook์˜ ๋ชฉํ‘œ๋Š” ์นœ๊ตฌ์˜ ์ƒํƒœ๋ฅผ ๊ตฌ๋…ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์ด๋ฅผ ์œ„ํ—ค friendID๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๊ณ  ์˜จ๋ผ์ธ ์ƒํƒœ์˜ ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

์ด์ œ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ์ด Hook ์„ ๊ฐ€์ง€๊ณ  ๋…๋ฆฝ์ ์œผ๋กœ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

Hook์€ state ๊ทธ ์ž์ฒด๊ฐ€ ์•„๋‹ˆ๋ผ, ์ƒํƒœ ๊ด€๋ จ ๋กœ์ง์„ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๊ฐ ์ปดํฌ๋„ŒํŠธ์—์„œ์˜ Hook ํ˜ธ์ถœ์€ ์™„์ „ํžˆ ๋…๋ฆฝ๋œ state๋ฅผ ๊ฐ€์ง€๊ณ , ์‹ฌ์ง€์–ด๋Š” ํ•œ ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ ๊ฐ™์€ custom Hook์„ ๋‘ ๋ฒˆ ์“ธ ์ˆ˜๋„ ์žˆ๋‹ค.

 

Custom Hook์€ ๊ธฐ๋Šฅ์ด๋ผ๊ธฐ๋ณด๋‹ค๋Š” ์ปจ๋ฒค์…˜(convention)์— ๊ฐ€๊น์Šต๋‹ˆ๋‹ค. ์ด๋ฆ„์ด ”use“๋กœ ์‹œ์ž‘ํ•˜๊ณ , ์•ˆ์—์„œ ๋‹ค๋ฅธ Hook์„ ํ˜ธ์ถœํ•œ๋‹ค๋ฉด ๊ทธ ํ•จ์ˆ˜๋ฅผ custom Hook์ด๋ผ๊ณ  ๋ถ€๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. useSomething์ด๋ผ๋Š” ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜์€ linter ํ”Œ๋Ÿฌ๊ทธ์ธ์ด Hook์„ ์ธ์‹ํ•˜๊ณ  ๋ฒ„๊ทธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. - React ๊ณต์‹ ๋ฌธ์„œ

 

๊ทธ๋ž˜์„œ ์ € ์‚ฌ์šฉ์ž ์ •์˜ Hook ์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์„œ๋กœ ๋‹ค๋ฅธ ๋‘ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);
  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);
  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

Reference

React ๊ณต์‹ ๋ฌธ์„œ

ko.reactjs.org/docs/hooks-overview.html

ko.reactjs.org/docs/hooks-effect.html

ko.reactjs.org/docs/hooks-custom.html

.