Hooks

황준승·2022년 1월 21일
0
post-thumbnail

목차

  1. useEffect
  2. useReducer
  3. useCallback
  4. useMemo
  5. useRef
  6. 커스텀 훅
  7. 실습 예제

출처

리액트를 다루는 기술 6장
useCallback과 useMemo 차이점

1. useEffect

useEffect는 리액트 컴포넌트가 렌더링될 때 마다 특정 작업을 수행하도록 설정할 수 있습니다.

클래스형 컴포넌트의 componentDidMount와 componentDidUpdate를 합친 형태로 보아도 무방합니다.

componentDidMount

useEffect(() => {
  console.log('마운트될 때만 실행됩니다.')
},[]);

이처럼 두 번째 파라미터에 아무것도 넣지 않을 경우 componentDidMount만 실행됩니다.

componentDidUpdate

useEffect(() => {
  console.log(name)
},[name]);

특정 값이 업데이트 될 때만 실행하고 싶을 때, 실행됩니다. 위에 코드는 해당 컴포넌트에서 name의 값이 바뀔 때마다 리렌더링 동작을 수행합니다.

언마운트를 수행하고 싶을 때

useEffect(()=>{
  console.log('effect');
  
  return () => {
    console.log('cleanup')
  }
},[])

이처럼 컴포넌트가 언마운트 되기 전이나 업데이트되기 직전에 어떠한 작업을 수행하고 싶다면 useEffect에서 return값에 새로운 콜백함수를 선언해주면 됩니다.

2. useReducer

기존의 리덕스로 프로젝트 경험이 있다면 쉽게 이해할 수 있을 것이다. 리덕스를 사용하기 위해서 type, action, 그리고 reducer를 설정해주어야 한다.

이때 type은 명령, action은 명령에 따른 action, 그리고 reducer는 이를 종합하여 설정하는 함수라고 보면 된다.

그리고 우리가 설정한 리듀서를 dispatch를 통해서 실행시킬 수 있다.

아래 리듀서 함수를 한 번 보자

function reducer(state, action){
  switch(action.type){
    case 'INCREASE':
    return {value: state.value + 1}
  }
}

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, {value:0});
}

리액트 공식문서에서 보면 useReducer는 useState를 대체할 수 있다라고 나옵니다.

해당 코드는 useState를 사용하는 것 보다 깔끔해보이지는 않습니다. 하지만 이것은 이벤트 핸들러 함수를 분리한다던지, 커스텀 훅을 만든다던지 등으로 어떻게 구현하냐에 따라 다르게 느껴질거 같습니다. 또한 나중에는 context API와 연동해서 redux를 완전히 대체하는데 쓰일 수 있지 않을까란 생각이 듭니다.

3. useCallback

이는 최적화를 위한 훅입니다. React에서 이벤트를 핸들링 할 때 보통 다음 코드처럼 컴포넌트 내부에 함수를 선언해서 사용합니다.

const App = () => {
  const handleClick = () => { console.log() }
  
  return (
  	<>
    	...  
    </>
  )

}

위 코드는 별로 문제가 되지 않습니다만 컴포넌트가 렌더링 될 때 마다 함수를 새로 생성한다는 단점이 있습니다. 부모 컴포넌트가 렌더링 되거나, 상태(state)가 변경되는 경우, React React 컴포넌트는 렌더링을 유발합니다.

하지만 아래코드 처럼 useCallback을 사용한다면 해당 함수를 메모이제이션을 이용해 함수 자체를 저장하고 이를 다시 사용함으로써 함수를 매번 새로 생성하는 수고를 덜 수 있습니다.

  const handleClick = React.useCallback(() => { console.log() })

useCallback의 두번째 인자

useEffect와 마찬가지로 의존성을 의미합니다.

예제

const handleClick = React.useCallback(
  () => console.log('current count :' + count),
[])

출력 결과

handleClick() // 실제 count 값: 0, 출력 결과: current count :0
setCount(count + 1) // 실제 count 값: 1
handleClick() // 실제 count 값: 1, 출력 결과: current count :0

두번째 호출에서 실제 count 값이 1증가해서 변경되었음에도 최초값인 0을 출력합니다. useCallback 내부에서 count값을 의존하지만, 이를 제대로 인지하지 못하고 이전 값을 출력하는 것입니다.

따라서 다음과 같이 useCallback의 두번째 인자 배열에 의존하는 상태값을 명시해야 제대로 동작합니다.

해결 예시

const handleClick = React.useCallback(
  () => console.log('current count :' + count),
[count]) // 의존하는 상태 명시

4. useMemo

useMemo 또한 useCallback과 마찬가지로 최적화에 사용됩니다. 다만 차이점이 있다면 useCallback은 함수를 반환하는 반면, 이것은 값을 반환합니다.

const memoizedValue = useMemo(() => getAvg(a,b) , [a,b])

인자로 함수와 Dependencies를 넘겨받게 되는데, 두번째 인자배열 안의 값이 하나라도 바뀌게 된다면 첫번째 인자의 함수가 재실행됩니다.

주의사항

모든 함수를 useMemo로 감싸게 된다면 이또한 리소스 낭비가 될 수 있으므로, 퍼포먼스 최적화가 필요한 연산량이 많은 곳에 사용하는 것읻 좋다.

어떨 때 useMemo와 useCallback을 사용하면 좋을까??

useMemo는 함수의 연산량이 많을 때 이전 결과값을 재사용하는 목적, useCallback은 함수가 재생성되는 것을 방지하는 목적이다.

내 생각

이건 순전히 내 생각이지만 componenet안에 선언된 함수들 같은 경우 useCallback을 사용하여 최적화를 하는 것이 좋다고 생각한다.

하지만 component 밖에 선언된 함수들 같은 경우 + 연산량과 시간이 오래 걸리는 함수일 경우 useMemo를 사용하여 해당 값이 함수에 의해 변경될 때만 사용하는 것이 좋아보인다.

5. useRef

사용법

  1. 클래스 문법 react에서 React.createRef와 사용법이 동일한다.
  2. 로컬 변수로 사용할 수 있다.
  • hooks 문법은 로컬 변수 사용에 제약이 많이 있는데 이때 useRef를 사용하게 된다면 쉽게 사용할 수 있다.

6. 커스텀 훅

여러 컴포넌트에서 비슷한 기능을 공유할 경우, 우리가 임의로 작성한 hooks를 작성하여 로직을 재사용할 수 있습니다.

오픈소스

7. 실습 예제

https://codesandbox.io/s/musing-http-nzpmc?file=/src/App.js

profile
다른 사람들이 이해하기 쉽게 기록하고 공유하자!!

0개의 댓글