React Custom Hooks (2)

HanSungUk·2022년 7월 28일
0

React

목록 보기
12/13
post-thumbnail
post-custom-banner

React Custom Hooks (2)

현재 코드스테이츠 강의를 통해 프론트엔드를 학습하고 있습니다.
본 포스트는 해당 강의에 대한 내용 정리를 목적으로 합니다.

학습목표

  • 함수 컴포넌트와 클래스 컴포넌트의 차이를 학습하고, 함수 컴포넌트에서 Hook을 사용하는 이유를 이해합니다.
  • Hook의 사용 규칙에 대해 학습하고 이해합니다.
  • useMemo의 쓰임새와 작성 방법에 대해 학습합니다.
  • useCallback의 쓰임새와 작성 방법에 대해 학습합니다.
  • custom hooks의 쓰임새와 작성 방법에 대해 학습니다.

1. Component와 Hook

Hook은 함수 컴포넌트에서 사용하는 메서드입니다.

function Counter () {
    const [counter, setCounter] = useState(0);

    const handleIncrease = () => {
        setCounter(counter + 1)
    }

    return (
        <div>
            <p>You clicked {counter} times</p>
            <button onClick={handleIncrease}>
                Click me
            </button>
        </div>
    )
}

Counter컴포넌트에서 useState() Hook을 호출해 함수 컴포넌트(function component)안에 state를 추가한 형태입니다. 이 state는 컴포넌트가 리렌더링 되어도 그대로 유지됩니다.

Hook 사용 규칙

  1. 리액트 함수의 최상위에서만 호출해야 합니다.
    반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하면 예상한 대로 동작하지 않을 우려가 있습니다.
    React는 이 Hook을 호출되는 순서대로 저장해놓기 때문에 조건문, 반복문 안에서 예기치 못한 버그가 발생할 수 있습니다.

  2. 오직 리액트 함수 내에서만 사용되어야 합니다.
    이는 리액트 함수형 컴포넌트나 커스텀 Hook이 아닌 다른 일반 JavaScript 함수 안에서 호출해서는 안 된다는 의미입니다.
    애초에 Hook은 React의 함수 컴포넌트 내에서 사용되도록 만들어진 메소드이기 때문에 근본적으로 일반 JavaScript 함수 내에서는 정상적으로 돌아가지 않습니다.

2. useMemo

React Hook은 함수 컴포넌트가 상태를 조작하고 최적화 기능을 사용할 수 있게끔하는 메소드입니다. 그 중 렌더링 최적화를 위한 Hook도 존재하는데, useCallback과 useMemo가 바로 그 역할을 하는 Hook입니다.

useMemo는 특정 값(value)를 재사용하고자 할 때 사용하는 Hook입니다.

function Calculator({value}){

	const result = calculate(value);

	return <>
      <div>
					{result}
      </div>
  </>;
}

calculate가 내부적으로 복잡한 연산을 해야하는 함수라고 가정한다면 해당 컴포넌트는 렌더링할 때마다 이 함수를 계속해서 호출할 것이고, 시간이 몇 초 이상 소요될 것입니다.

/* useMemo를 사용하기 전에는 꼭 import해서 불러와야 합니다. */
import { useMemo } from "react";

function Calculator({value}){

	const result = useMemo(() => calculate(value), [value]);

	return <>
      <div>
					{result}
      </div>
  </>;
}

이런 식으로 useMemo를 호출하여 calculate 함수를 감싸주면, 이전에 구축된 렌더링과 새로이 구축되는 렌더링을 비교해 value값이 동일할 경우에는 굳이 calculate 함수를 호출할 필요도 없이 이전 렌더링의 result값을 그대로 재활용할 수 있게 됩니다. 이는 메모이제이션(Memoization)개념과 긴밀한 관계가 있습니다.

메모이제이션(Memoization)은 기존에 수행한 연산의 결과값을 메모리에 저장해두고, 동일한 입력이 들어오면 재활용하는 기법을 말합니다. 굳이 중복 연산을 할 필요가 없기 때문에 앱의 성능을 최적화할 수 있습니다.

3. useCallback

useCallback 또한 useMemo와 마찬가지로 메모이제이션 기법을 이용한 Hook입니다. useMemo는 값의 재사용을 위해 사용하는 Hook이라면, useCallback은 함수의 재사용을 위해 사용하는 Hook입니다.

function Calculator({x, y}){

	const add = () => x + y;

	return <>
      <div>
					{add()}
      </div>
  </>;
}

add함수는 props로 넘어온 xy값을 더해<div>태그에 값을 출력합니다.
이 함수는 해당 컴포넌트가 렌더링 될 때마다 새롭게 만들어집니다.

useCallbackuseMemo처럼 함수를 호출하지 않는 Hook이 아니라
메모리 어딘가에 함수를 꺼내서 호출하는 Hook입니다.
따라서, 단순히 컴포넌트 내에서 함수를 반복해서 생성하지 않기 위해 useCallback을 사용하는 것은 큰 의미가 없거나 오히려 손해인 경우도 있습니다.

useCallback을 사용하기 좋은 경우는 자식 컴포넌트의 props로 함수를 전달해줄 때입니다.

/* useCallback를 사용하기 전에는 꼭 import해서 불러와야 합니다. */
import React, { useCallback } from "react";

function Calculator({x, y}){

	const add = useCallback(() => x + y, [x, y]);

	return <>
      <div>
					{add()}
      </div>
  </>;
}

useCallback은 참조 동등성에 의존합니다. React는 JavaScript 언어로 만들어진 오픈소스 라이브러리이기 때문에 기본적으로 JavaScript의 문법을 따라갑니다.

JavaScript에서 함수는 객체입니다. 객체는 메모리에 저장할 때 값을 저장하는 게 아니라 값의 주소를 저장하기 때문에 반환하는 값이 같을 지라도 일치연산자로 비교했을 때 false가 출력됩니다.

function doubleFactory(){
    return (a) => 2 * a;
}
  
const double1 = doubleFactory();
const double2 = doubleFactory();
  
double1(8); // 16
double2(8); // 16
  
double1 === double2;  // false
double1 === double1;  // true

이는 React 또한 같습니다. React는 리렌더링 시 함수를 새로이 만들어서 호출합니다. 새로이 만들어 호출된 함수는 기존의 함수와 같은 함수가 아닙니다. 따라서 React 컴포넌트 함수 내에서 다른 함수의 인자로 넘기거나 자식 컴포넌트의 Prop으로 넘길 때 예상치 못한 성능 문제를 막을 수 있습니다.

useCallback을 이용해 함수 자체를 저장해서 다시 사용하면 함수의 메모리 주소 값을 저장했다가 다시 사용한다는 것과 같다고 볼 수 있습니다.

4. Custom Hooks

개뱔자가 스스로 커스텀한 훅을 의미하며 이를 이용한 반복되는 로직을 함수로 뽑아내어 재사용할 수 있습니다.

여러 url을 fetch할 때, 여러 input에 의한 상태 변경 등 반복되는 로직을 동일한 함수에서 작동하게 하고 싶을 때 커스텀 훅을 주로 사용합니다.
장점으로는
1. 상태관리 로직의 재활용이 가능하다.
2. 클래스 컴포넌트보다 적은 양의 코드로 동일한 로직을 구현할 수 있다.
3. 함수형으로 작성하기 때문에 보다 명료하다.
가 있습니다.

Custom Hook을 정의할 때는 일종의 규칙이 필요합니다.

  • Custom Hook을 정의할 때는 함수 이름 앞에 use를 붙이는 것이 규칙입니다.
  • 대개의 경우 프로젝트 내의 hooks 디렉토리에 Custom Hook을 위치 시킵니다.
  • Custom Hook으로 만들 때 함수는 조건부 함수가 아니어야 합니다. 즉 return 하는 값은 조건부여서는 안됩니다.

일반 함수 내부에서는 React 내장 Hook을 불러 사용할 수 없지만 Custom Hook에서는 가능합니다.

  • Custom Hook의 예시
    [코드] 여러 url을 fetch할 때 쓸 수 있는 useFetch Hook
const useFetch = ( initialUrl:string ) => {
	const [url, setUrl] = useState(initialUrl);
	const [value, setValue] = useState('');

	const fetchData = () => axios.get(url).then(({data}) => setValue(data));	

	useEffect(() => {
		fetchData();
	},[url]);

	return [value];
};

export default useFetch;

[코드] 여러 input에 의한 상태 변경을 할 때 쓸 수 있는 useInputs Hooks

import { useState, useCallback } from 'react';

function useInputs(initialForm) {
  const [form, setForm] = useState(initialForm);
  // change
  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;
post-custom-banner

0개의 댓글