Hook

July 17, 2021 · 14 mins read label-icon React

Hook

  • 클래슀 μ»΄ν¬λ„ŒνŠΈμ—μ„œμ˜ LifeCycle κ³Ό μƒνƒœκ΄€λ¦¬λ₯Ό ν•¨μˆ˜ν˜• μ»΄ν¬λ„ŒνŠΈμ—μ„œ 쉽고 κ°„νŽΈν•˜κ²Œ μ‚¬μš©ν•  수 μžˆλŠ” ν•¨μˆ˜
  • Hooks λŠ” React 16.8 버전뢀터 μ§€μ›ν•œλ‹€.

νŠΉμ§•

일반 Hook

useState

  • μ»΄ν¬λ„ŒνŠΈμ˜ μƒνƒœκ΄€λ¦¬ μ‹œ μ‚¬μš©λœλ‹€.
    1
    
    const [state, setState] = useState(initialState);
    
  • 졜초 λ Œλ”λ§ μ‹œ state 와 initialState λŠ” 값이 κ°™λ‹€.
  • setState() 둜 μƒνƒœκ°’(state)을 λ³€κ²½ν•  수 μžˆλ‹€.
  • μƒνƒœκ°’μ΄ λ³€κ²½λ˜λ©΄ μ»΄ν¬λ„ŒνŠΈμ˜ λ¦¬λ Œλ”λ§μ΄ μ§„ν–‰λœλ‹€.

useEffect

  • μ»΄ν¬λ„ŒνŠΈκ°€ λ Œλ”λ§μ΄ 될 λ•Œλ§ˆλ‹€ νŠΉμ • μž‘μ—…μ„ ν•  경우 μ‚¬μš©ν•œλ‹€.
1
useEffect(didUpdate);
  • 클래슀 μ»΄ν¬λ„ŒνŠΈμ˜ componentDidMount 와 componentDidUpdate λ₯Ό ν•©μΉœ κ±°λž‘ λΉ„μŠ·ν•˜λ‹€.
1
2
3
4
5
6
7
8
9
// componentDidMount
useEffect(() => {
    console.log('첫 λ Œλ”λ§!')
},[])

// componentDidUpdate
useEffect(() => {
    console.log('state 값이 변경될 λ•Œλ§ˆλ‹€ μ‹€ν–‰!')
},[state])
  • μ»΄ν¬λ„ŒνŠΈκ°€ μ–Έλ§ˆμš΄νŠΈλ˜κ±°λ‚˜ μ—…λ°μ΄νŠΈ 되기 직전에 μ–΄λ– ν•œ μž‘μ—…μ„ ν•˜κ³  싢은 경우 clean-up(정리) ν•¨μˆ˜λ₯Ό λ„£μ–΄μ€€λ‹€.
  • 정리 ν•¨μˆ˜λŠ” 주둜 λ©”λͺ¨λ¦¬ λˆ„μˆ˜ 방지 μ‹œ μ‚¬μš©λœλ‹€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// μ—…λ°μ΄νŠΈ & μ–Έλ§ˆμš΄νŠΈ
useEffect(() => {
    console.log('mount!')

    // clearn up
    return() => { 
        console.log('unmount!')
    }
})

// μ–Έλ§ˆμš΄νŠΈ 일 κ²½μš°μ—λ§Œ
useEffect(() => {
    console.log('mount!')

    // clearn up
    return() => { 
        console.log('unmount!')
    }
},[]) // λ‘λ²ˆμ§Έ μΈμžμ— 빈 λ°°μ—΄κ°’ μΆ”κ°€

useContext

  • ν•¨μˆ˜ν˜• μ»΄ν¬λ„ŒνŠΈμ—μ„œ Context λ₯Ό μ‚¬μš©ν•˜λŠ” 경우 μ‚¬μš©λœλ‹€.
  • Context λ₯Ό μ‚¬μš©ν•˜μ—¬ μƒνƒœκ°’μ„ μ „μ—­μœΌλ‘œ μ‚¬μš©ν•  수 μžˆλ‹€.

μΆ”κ°€ Hook

useReducer

1
const [state, dispatch] = useReducer(reducer, initialArg, init);
  • μ»΄ν¬λ„ŒνŠΈ μ™ΈλΆ€μ—μ„œ μƒνƒœ 관리λ₯Ό ν•˜λŠ” 경우 μ‚¬μš©ν•œλ‹€.
  • useState 의 λŒ€μ²΄ ν•¨μˆ˜
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

// component
function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}
  • initialState 와 reducer() λ₯Ό μ™ΈλΆ€ 파일둜 λΆ„λ¦¬ν•΄μ„œ μ‚¬μš©ν•  μˆ˜λ„ μžˆλ‹€.

useMemo

  • λ©”λͺ¨μ΄μ œμ΄μ…˜λœ 값을 λ°˜ν™˜ν•œλ‹€.
  • μ„±λŠ₯을 μ΅œμ ν™”ν•  λ•Œ μ‚¬μš©ν•œλ‹€.
  • ν•¨μˆ˜μ˜ 리턴값을 κΈ°μ–΅ν•˜μ—¬ νŠΉμ • 값이 λ³€κ²½λ˜μ—ˆμ„ κ²½μš°μ—λ§Œ μž¬μ—°μ‚°μ„ ν•œλ‹€.
    1
    
    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
    
  • 첫 번째 μΈμˆ˜μ—λŠ” ν•¨μˆ˜, 두 번째 μΈμˆ˜μ—λŠ” (μ˜μ‘΄μ„±)배열을 λ„£μ–΄μ€€λ‹€.
  • 두 번째 μΈμˆ˜μ— λ„£μ–΄μ€€ λ°°μ—΄μ˜ 값이 λ°”λ€ŒλŠ” κ²½μš°μ—λ§Œ 첫 번째 인수의 ν•¨μˆ˜κ°€ μ‹€ν–‰λœλ‹€.

useCallback

  • λ©”λͺ¨μ΄μ œμ΄μ…˜λœ 콜백(ν•¨μˆ˜)을 λ°˜ν™˜ν•œλ‹€.
  • μ»΄ν¬λ„ŒνŠΈκ°€ λ¦¬λ Œλ”λ§λ˜λ©΄ μ»΄ν¬λ„ŒνŠΈ μ•ˆμ— μžˆλŠ” ν•¨μˆ˜κ°€ μƒˆλ‘œ μƒμ„±λ˜μ–΄ μ„±λŠ₯에 쒋지 μ•ŠκΈ° λ•Œλ¬Έμ— 이λ₯Ό μ΅œμ ν™”ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•œλ‹€.
  • 주둜 λ Œλ”λ§μ΄ 자주 λ°œμƒν•˜λŠ” μ»΄ν¬λ„ŒνŠΈμ˜ μ΅œμ ν™”λ₯Ό μœ„ν•΄ μ‚¬μš©ν•œλ‹€.
1
2
3
4
5
6
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);
  • 첫 번째 μΈμˆ˜μ—λŠ” ν•¨μˆ˜, 두 번째 μΈμˆ˜μ—λŠ” (μ˜μ‘΄μ„±)배열을 λ„£μ–΄μ€€λ‹€.
  • 두 번째 μΈμˆ˜μ— λ„£μ–΄μ€€ λ°°μ—΄μ˜ 값이 λ°”λ€ŒλŠ” κ²½μš°μ—λ§Œ 첫 번째 인수의 ν•¨μˆ˜κ°€ μƒˆλ‘œ μƒμ„±λœλ‹€.

useRef

1
const refContainer = useRef(initialValue);
  • μ»΄ν¬λ„ŒνŠΈμ˜ HTML Element 에 μ ‘κ·Όν•˜λŠ” 경우 μ‚¬μš©ν•œλ‹€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function TextInputWithFocusButton() {
    const inputEl = useRef(null);

    const onButtonClick = () => {
    // current λŠ” ref 값이 inputEl 인 element λ₯Ό 가리킨닀.
    inputEl.current.focus();
    };

    return (
    <>
        <input ref={inputEl} type="text" />
        <button onClick={onButtonClick}>Focus the input</button>
    </>
    );
}
  • μ»΄ν¬λ„ŒνŠΈμ˜ λ‘œμ»¬λ³€μˆ˜λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•˜λŠ” 경우 μ‚¬μš©ν•œλ‹€.
  • ref μ•ˆμ˜ 값이 λ³€κ²½λ˜μ–΄λ„ λ Œλ”λ§μ΄ λ˜μ§€ μ•ŠλŠ”λ‹€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React, { useRef } from 'react';

const RefSample = () => {
    const id = useRef(1);

    const setId = (n) => {
        id.current = n;
    }

    const printId = () => {
        console.log(id.current);
    }
    
    return (
        <div>refsample</div>
    );
};

export default RefSample;

useImperativeHandle

  • λΆ€λͺ¨ μ»΄ν¬λ„ŒνŠΈμ—μ„œ μžμ‹ μ»΄ν¬λ„ŒνŠΈμ˜ λ©”μ„œλ“œ λ˜λŠ” ref 에 μ ‘κ·Όν•  경우 μ‚¬μš©ν•œλ‹€.
1
useImperativeHandle(ref, createHandle, [deps])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// children
import React, { useRef, forwardRef, useImperativeHandle } from "react";

function FancyInput(props, ref) {
  const inputRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));

  return <input ref={inputRef} />;
}

export default forwardRef(FancyInput);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// parent
import React, { useRef } from "react";
import FancyInput from "./fancyInput";

function ParentComp(props, ref) {
  const FancyInputRef = useRef();

  const handleClick = () => {
    if (FancyInputRef.current) {
      FancyInputRef.current.focus();
    }
  };

  return (
    <div>
      <FancyInput ref={FancyInputRef} />
      <button onClick={handleClick}>λ²„νŠΌ</button>
    </div>
  );
}

export default ParentComp;

useLayoutEffect

  • λ ˆμ΄μ•„μ›ƒ 배치(element) κ°€ μ§„ν–‰λ˜κΈ° 전에 μƒνƒœκ°’μ„ μ„€μ •ν•˜λŠ” 경우 μ‚¬μš©ν•œλ‹€.
  • μƒˆλ‘œκ³ μΉ¨ μ‹œ μƒνƒœκ°’μ΄ λ³€κ²½λ˜μ–΄ κΉœλΉ‘κ±°λ¦¬λŠ”(?) 이슈λ₯Ό ν•΄κ²°ν•  수 μžˆλ‹€.
  • useEffect() 와 ν˜•νƒœκ°€ κ°™λ‹€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
useLayoutEffect(() => {
  effect
  return () => {
    cleanup
  };
}, [input])


// ex
const [name, setName] = useState("");
const [age, setAge] = useState(0);

useLayoutEffect(() => {
    setAge(25);
    setName("sw");
}, []);

useDebugValue

  • React κ°œλ°œμžλ„κ΅¬μ—μ„œ μ‚¬μš©μž Hook λ ˆμ΄λΈ”μ„ ν‘œμ‹œν•˜λŠ” 데에 μ‚¬μš©ν•œλ‹€.
1
2
3
4
5
6
7
function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useDebugValue(isOnline ? 'Online' : 'Offline');

  return isOnline;
}

+Custom Hook

  • 각각 λ‹€λ₯Έ μ»΄ν¬λ„ŒνŠΈμ—μ„œ 자주 μ‚¬μš©λ˜λŠ” λ‘œμ§μ„ hook 으둜 λ§Œλ“€μ–΄μ„œ μ‚¬μš©ν•  수 μžˆλ‹€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// useInputs
import { useState, useCallback } from 'react';

function useInputs(initialForm) {
  const [form, setForm] = useState(initialForm);
  
  const onChange = useCallback(e => {
    const { name, value } = e.target;
    setForm(form => ({ ...form, [name]: value }));
  }, []);

  const reset = useCallback(() => setForm(initialForm), [initialForm]);

  return [form, onChange, reset];
}

export default useInputs;
  • input 을 μž…λ ₯ν•  λ•Œ μƒνƒœκ°’μ„ λ³€κ²½ν•΄μ£ΌλŠ” useInput 이닀.

μ°Έκ³ 

https://ko.reactjs.org/docs/hooks-intro.html
https://velog.io/@velopert/react-hooks