๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
  • What would life be If we had no courage to attemp anything?
Development/React.js

[React] React Hook API

by DevIseo 2023. 1. 18.

Hook ๊ฐœ์š”

  • Hook์€ class๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ ๋„ state์™€ ๋‹ค๋ฅธ React์˜ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์คŒ
  • Hook์€ ๊ณ„์ธต์˜ ๋ณ€ํ™” ์—†์ด ์ƒํƒœ ๊ด€๋ จ ๋กœ์ง์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์คŒ

๊ธฐ๋ณธ Hook

  • useState
  • useEffect
  • useContext

์ถ”๊ฐ€ Hooks

  • useReducer
  • useCallback
  • useMemo
  • useRef
  • useImperativeHandle
  • useLayoutEffect
  • useDebugValue
  • useDeferredValue
  • useTransition
  • useId

library hooks

  • useSyncExternalStore
  • useInsertionEffect

๐Ÿ“ŒState Hook

์—ฌ๋Ÿฌ state ๋ณ€์ˆ˜ ์„ ์–ธํ•˜๊ธฐ

ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ state hook์„ ์—ฌ๋Ÿฌ๊ฐœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ

function ExampleWithManyStates() {
  // ์ƒํƒœ ๋ณ€์ˆ˜๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ์„ ์–ธ
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}

HOOK์ด๋ž€?

  • ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์—์„œ React state์™€ ์ƒ๋ช…์ฃผ๊ธฐ ๊ธฐ๋Šฅ์„ ์—ฐ๋™ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜
  • class ์•ˆ์—์„œ๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š์Œ

โšก๏ธ Effect Hook

  • useEffect๋Š” ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ์ด๋Ÿฐ side effects๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•จ
  • React class์˜ componentDidMount๋‚˜ componentDidUpdate, componentWillUnmount์™€ ๊ฐ™์€ ๋ชฉ์ ์œผ๋กœ ์ œ๊ณต๋˜๋Š” ๊ฒƒ์„ ํ•˜๋‚˜์˜ API๋กœ ํ†ตํ•ฉ
  • ์‹ค์Šต

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

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

  // componentDidMount, componentDidUpdate์™€ ๋น„์Šท
  useEffect(() => {
    // ๋ธŒ๋ผ์šฐ์ € API๋ฅผ ์ด์šฉํ•ด ๋ฌธ์„œ์˜ ํƒ€์ดํ‹€์„ ์—…๋ฐ์ดํŠธ!
    document.title = `clicked ${count} times`;
  });

  return (
    <div>
      <p>clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        click
      </button>
    </div>
  );
}
  • Effect ํ•ด์ œ
    • ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ (optional)
    • ex) ์ปดํฌ๋„ŒํŠธ - ์นœ๊ตฌ์˜ ์ ‘์† ์ƒํƒœ๋ฅผ ๊ตฌ๋…ํ•˜๋Š” effect ์‚ฌ์šฉ⇒ ๊ตฌ๋…์„ ํ•ด์ง€ํ•จ์œผ๋กœ์จ ํ•ด์ œ
import React, { useState, useEffect } from 'react';

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

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);    
		return () => {
			 ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);    };  
});
if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
  • useEffect ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ effect๋ฅผ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ์Œ
  • Hook์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ตฌ๋…์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์ œ๊ฑฐํ•˜๋Š” ๋กœ์ง๊ณผ ๊ฐ™์ด ์„œ๋กœ ๊ด€๋ จ ์žˆ๋Š” ์ฝ”๋“œ๋“ค์„ ํ•œ๊ตฐ๋ฐ์— ๋ชจ์•„์„œ ์ž‘์„ฑ
    • class ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ(lifecycle methods) ๊ฐ๊ฐ์— ์ชผ๊ฐœ์„œ ๋„ฃ์–ด์•ผ ํ•จ

โœŒ๏ธHOOK ์‚ฌ์šฉ ๊ทœ์น™

  1. ์ตœ์ƒ์œ„์—์„œ๋งŒ ํ˜ธ์ถœ
    • ๋ฐ˜๋ณต๋ฌธ, ์กฐ๊ฑด๋ฌธ, ์ค‘์ฒฉ๋œ ํ•จ์ˆ˜ ๋‚ด์—์„œ hook ์‹คํ–‰ ๊ธˆ์ง€
  2. React ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ๋งŒ hook์„ ํ˜ธ์ถœ
    • ์ผ๋ฐ˜ js ํ•จ์ˆ˜์—์„œ๋Š” hook์„ ํ˜ธ์ถœ ํ•˜๋ฉด ์•ˆ๋จ

State Hook ์‚ฌ์šฉํ•˜๊ธฐ

Hook์ด๋ž€?

  • React์˜ useState Hook
import React, { useState } from 'react';
function Example() {
  // ...
}

state ๋ณ€์ˆ˜ ์„ ์–ธํ•˜๊ธฐ

  • class๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ
    • constructor์•ˆ์— this.state๋ฅผ {count:0} ๋กœ ์„ค์ •
    • count๋ฅผ 0์œผ๋กœ ์ดˆ๊ธฐํ™”
    class Exam extends React.component{
    	constructor(props){
    		super(props)
    		this.state={
    			count:0
    		}
    	}
    
  • ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ
    • this๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์—†์Œ ⇒ this.state๋ฅผ ํ• ๋‹นํ•˜๊ฑฐ๋‚˜ ์ฝ์„ ์ˆ˜ ์—†์Œ
    • useState Hook์„ ์ง์ ‘ ์ปดํฌ๋„ŒํŠธ์— ํ˜ธ์ถœ
    import React, { useState } from 'react';
    function Example() {
      // ์ƒˆ๋กœ์šด state ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๊ณ , ์ด๊ฒƒ์„ count๋ผ ๋ถ€๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.
      const [count, setCount] = useState(0);
    

state ๊ฐ€์ ธ์˜ค๊ธฐ

  • ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ
    • this.state.count ์‚ฌ์šฉ
    <p>You clicked {this.state.count} times</p>
    
  • ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ
    • count ์ง์ ‘ ์‚ฌ์šฉ
    <p>You clicked {count} times</p>
    

state ๊ฐฑ์‹ ํ•˜๊ธฐ

  • ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ
    • this.setState() ํ˜ธ์ถœ
    <button onClick={() => this.setState({ count: this.state.count + 1 })}>
        Click me
      </button>
    
  • ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ
    • setCount์™€ count ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฏ€๋กœ this๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์•„๋„ ๋จ
    <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    

Effect Hook ์‚ฌ์šฉํ•˜๊ธฐ

  • ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ, ๊ตฌ๋…(subscription)์„ค์ •ํ•˜๊ธฐ, ์ˆ˜๋™์œผ๋กœ React ์ปดํฌ๋„ŒํŠธ์˜ DOM์„ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ
  • class ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ์—์„œ componentDidMount์™€ componentDidUpdate, componentWillUnmount๊ฐ€ ํ•ฉ์ณ์ง„ ๊ฒƒ

Hook์„ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์‹œ

  • useEffect๊ฐ€ ํ•˜๋Š” ์ผ์€?
    • React์—๊ฒŒ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ์ดํ›„์— ์–ด๋–ค ์ผ์„ ์ˆ˜ํ–‰ํ•˜๋Š”๊ฐ€
    • ์šฐ๋ฆฌ๊ฐ€ ๋„˜๊ธด ํ•จ์ˆ˜(effect)๋ฅผ ๊ธฐ์–ตํ–ˆ๋‹ค๊ฐ€ DOM ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•œ ์ดํ›„์— ๋ถˆ๋Ÿฌ๋ƒ„
  • useEffect๋ฅผ ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ ๋ถˆ๋Ÿฌ๋‚ด๋Š” ์ด์œ ๋Š”?
    • ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ๋‘ ์œผ๋กœ์จ effect๋ฅผ ํ†ตํ•ด count state ๋ณ€์ˆ˜(๋˜๋Š” ์–ด๋–ค prop)์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Œ
    • ํ•จ์ˆ˜ ๋ฒ”์œ„์•ˆ์— ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํŠน๋ณ„ํ•œ API ์—†์ด๋„ ๊ฐ’์„ ์–ป์„ ์ˆ˜ ์žˆ์Œ
    • hook์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ํด๋กœ์ €๋ฅผ ์ด์šฉํ•˜์—ฌ react์— ํ•œ์ •๋œ API๋ฅผ ๊ณ ์•ˆํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ด๋ฏธ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์ด์šฉํ•ด ๋ฌธ์ œ ํ•ด๊ฒฐ
  • useEffect๋Š” ๋ Œ๋”๋ง ์ดํ›„์— ๋งค๋ฒˆ ์ˆ˜ํ–‰?
    • ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฒซ๋ฒˆ์งธ ๋ Œ๋”๋ง๊ณผ ์ดํ›„์˜ ๋ชจ๋“  ์—…๋ฐ์ดํŠธ์—์„œ ์ˆ˜ํ–‰
    • effect๊ฐ€ ์ˆ˜ํ–‰๋˜๋Š” ์‹œ์ ์— ์ด๋ฏธ DOM ์ด ์—…๋ฐ์ดํŠธ

Effect ์ •๋ฆฌ

  • ํ•„์š”ํ•œ ๊ฒฝ์šฐ
    • ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜
useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  • ํ•„์š” ์—†๋Š” ๊ฒฝ์šฐ
    • ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์Œ
useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

Effect์˜ ์ตœ์ ํ™”

  • useEffect Hook API์— ๋‚ด์žฌํ™”
  • ํŠน์ • ๊ฐ’๋“ค์ด ๋ฆฌ๋ Œ๋”๋ง ์‹œ์— ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์œผ๋ฉด, react๋กœ ํ•˜์—ฌ๊ธˆ effect ๊ฑด๋„ˆ ๋›ธ ์ˆ˜ ์žˆ์Œ
    • useEffect ์˜ ์„ ํƒ์  ์ธ์ˆ˜์ธ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ๋ฐฐ์—ด์„ ๋„˜๊น€
useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // count๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งŒ effect๋ฅผ ์žฌ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

useContext

  • Context๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ „์—ญ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ 
    • ์ค‘๊ฐ„ ๋‹ค๋ฆฌ ์—ญํ• ๋งŒ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์„ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ”๋กœ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅ
  • ๋ฆฌ์•กํŠธ Hook์ธ useContext๋Š” ์ด๋Ÿฌํ•œ Context๋ฅผ ์ข€ ๋” ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์—ญํ• 
  • useContext๋กœ ์ „๋‹ฌํ•œ ์ธ์ž๋Š” context ๊ฐ์ฒด ๊ทธ ์ž์ฒด์ด์–ด์•ผ ํ•จ

import HeaderBar from '../components/header/HeaderBar'
import React,{useState,useEffect, useContext} from'react'

const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222"
  }
};

const ThemeContext = React.createContext(themes.light);

function App() {
  const [count, setCount] = useState(0)

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

  return (
    <div className="App">
      <HeaderBar/>
      <p>clicked {count} times</p>
      <button onClick={()=>setCount(count+1)}>
        click
      </button>
      **<ThemeContext.Provider value={themes.dark}>
        <Tool/>
      </ThemeContext.Provider>**
    </div>
  );
}

function Tool(props){
  return(
    <div>
      <ThemedButton/>
    </div>
  )
}

function ThemedButton(){
  const theme = useContext(ThemeContext)
  return(
    <button style={{background:theme.background,color:theme.foreground}}>
      theme context
    </button>
  )
}
export default App;

๋Œ“๊ธ€