React Hooks

모성종·2022년 7월 31일
0

리액트의 함수형 컴포넌트에서 사용하는 Hooks을 정리해보자

리액트 훅(React Hooks)이란?

Hook은 함수형 컴포넌트에서 리액트 state와 생명주기 기능(lifecycle features)을 "연동(hook into)"할 수 있게 해주는 함수이다.

useState

함수형 컴포넌트에서 사용하는 state를 변화할 수 있도록 제공되는 훅이다. 리액트에서 state를 직접 변경하는 것은 불가하기 때문에 클래스형 컴포넌트에서도 setState가 제공되는 것처럼 함수형 컴포넌트에도 있다.
useState를 사용해서 상태를 변화시킨 경우 해당 컴포넌트가 다시 렌더링된다.
useState는 상태변수와, 그 상태값을 변화시킬 수 있는 함수가 반환된다.

import React, { useState } from 'react'

const App = () => {
  const [name, setName] = useState('');
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { value } = e.target.value;
    setName(() => value);
  }
  return (
    <>
      <input type="text" name="name" onChange={handleChange} value={name}/>
    </>
  );
}

export default App

useEffect

리액트 함수형 컴포넌트가 실행해서 렌더링될 때 컴포넌트에 작용하는 effect함수이다.
클래스형 컴포넌트에서 수행하던 Life-Cycle 중 componentDidMount, componentDidUpdate, componentWillUnmount를 통합하여 사용할 수 있는 Hook이다.
나 ,
컴포넌트가 렌더링된 후 useEffect에 전달된 콜백함수가 실행된다.
두번째 인자에 특정 값이나 함수를 배열로 전달하여 dependencies 에 맞게 선택적으로 effect 콜백함수를 실행할 수 있다.

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

const App = () => {
  const [name, setName] = useState('');
  useEffect(() => {
    console.log('useEffect 실행1');
    return () => {
      console.log('useEffect clean up');
    }
  });
  
  useEffect(() => {
    console.log('useEffect 실행2');
  }, []);
  
  useEffect(() => {
    console.log('useEffect 실행3');
  }, [name]);
  
  return (
    <>
      <input type="text" name="name" onChange={handleChange} value={name}/>
    </>
  );
}

export default App
  • 'useEffect 실행1'은 전달된 의존값이 없기 때문에 렌더링과 리렌더링때마다 콜백함수가 실행된다.
  • 'useEffect 실행2'는 의존배열에 아무값도 넘겨주지 않았기 때문에 최초에 1번만 콜백함수가 실행된다.
  • 'useEffect 실행3'은 의존배열에 name 상태를 넘겨줬기 때문에 name의 상태값이 변했을 때만 콜백함수가 실행된다.

useEffect 콜백함수에서 clean up 함수를 반환하여 해당 컴포넌트가 unmount될 때 클린업함수를 실행해서 해당 컴포넌트에서 사용하는 외부라이브러리나 모듈 등과 같이 리소스를 정리할 수 있다.

useContext

React.createContext 와 함께 사용되는 훅이다. 리액트는 단방향 데이터 flow를 갖는데, props drilling이라고도 한다. 상위 컴포넌트 -> 하위 컴포넌트로 props를 전달하는 형태로 데이터를 전달한다.
이러한 특성은 컴포넌트가 많이 중첩된 경우 props를 전달하기 위해 불필요하게 중간에 거치는 컴포넌트가 많아질 수 있다는 것이다.

import React, { createContext, useContext } from 'react'

const themes = {
  light: {
    background: '#fff'
  },
  dark: {
    background: '#222'
  }
}
const ThemeContext = createContext(themes.light)

const App = () => {
  return (
    <>
      <ThemeContext.Provider value={themes.dark} >
      	<Child />
      </ThemeContext.Provider>
    </>
  );
}

const Child = () => {
  const theme = useContext(ThemeContext);
  return (
    <>
      <div style={{ background: theme.background }}>
        this is child
      </div>
    </>
  );
}

컴포넌트의 중첩이 얼마가 되든 useContext(컨텍스트 객체);를 사용하면 가장 가까운 context provider가 사용된다.

useRef

const refContainer = useRef(initialValue);

useRef는 initialValue로 초기화된 ref객체를 반환한다. 이 ref객체는 변경가능하며 .current 프로퍼티로 접근할 수 있다.

import React, { useRef } from 'react'

const InputWithFocusButton = () => {
  const inputEl = useRef(null);
  const handleClick = () => {
    inputEl.current.focus();
  }
  return (
    <>
      <input type="text" ref={inputEl} />
      <button onClick={handleClick}>버튼에 포커스</button>
    </>
  );
}

inputEl은 null로 초기화된 컨테이너이고 렌더링 시 input Element를 담았다.
버튼 클릭할 때 inputEl.current 로 input DOM에 접근하여 focus API를 호출했다.

useReducer

useState의 대체함수이다.

const [state, dispatch] = useReducer(reducer, initialState);

reducer는 (state, action) => newState 형태를 갖는 함수이다.
다수의 하윗값을 포함하는 복잡한 정적 로직을 만드는 경우나 다음 state가 이전 state에 의존적인 경우에 보통 useState보다 useReducer를 선호한다.

import React, { useReducer } from 'react'

const initialState = { count: 0 }

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

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state}
      <button onClick={() => dispatch({ type: 'increment' })}> + </button>
      <button onClick={() => dispatch({ type: 'decrement' })}> - </button>
    </>
  );
}

useCallback

의존배열을 기준으로 메모이제이션된 콜백함수를 반환한다.

const memoCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

리액트 함수형 컴포넌트가 렌더링될 때 내부에 선언한 함수는 기능이 동일하더라도 그 함수의 참조값이 변경돼서 새로운 참조값(주소값)이 할당된다. 그래서 useCallback을 사용한 내부함수를 자식 컴포넌트에게 props로 전달하여 컴포넌트 렌더링 성능최적화를 할 수 있다.

useMemo

의존배열을 기준으로 메모이제이션된 값을 반환한다.

const memoValue = useMemo(() => doSomething(a, b), [a, b]);

useCallback과 마찬가지로 의존배열에 있는 값이 변경될 때만 useMemo 콜백함수를 재실행하여 새로운 값을 반환한다.

useCallbackuseMemo로 사용된 함수와 값을 자식 컴포넌트에게 전달한 경우, 자식 컴포넌트를 React.memo 로 사용하여 감싸면 memoized component가 되어 렌더링 성능최적화를 할 수 있다.

profile
FE Developer

0개의 댓글