[React] React Hook

숑이·2024년 7월 24일
0

리액트공부하기

목록 보기
3/4
post-thumbnail

리액트 훅이란?

리액트 훅은 리액트 클래스형 컴포넌트에서 이용하던 코드를 작성할 필요없이 함수형 컴포넌트에서 다양한 기능을 사용할 수 있게 만들어준 라이브러리, React 16.8버전에 새로 추가된 기능이다. 이는 함수형 컴포넌트에 맞게 만들어진 것으로 함수형 컴포넌트에서만 사용 가능하다.

React에서 use로 시작하는 함수를 Hook이라고 부른다.

Hook의 규칙

1) Hook을 최상위 레벨에서만 호출한다.

  • Hook을 반복문, 조건문, 중첩 함수 내에서 Hook을 호출하면 안된다.
function Counter(){
  //함수 컴포넌트의 최상위 레벨에서 사용한다. 
  const [count, setCount] = useState(0);
  //...
}

function useWindowWidth(){
  //커스텀 Hook의 최상위 레벨에서 사용한다.
	const[width, setWidth] = useState(window.innerWidth);
  //...
}

2) 리액트 함수 내에서만 Hook을 호출해야한다.

  • Hook은 일반적인 js 함수에서는 호출하면 안된다.
  • 함수형 컴포넌트나 custom hook에서는 호출 가능한다.

자주 사용하는 React Hook

1. useState

useState는 상태를 관리하는 훅으로 컴포넌트에 state변수를 추가할 수 있는 훅이다.

const [state, setState] = useState(initialState)
  • 함수의 파라미터에는 상태의 기본값을 넣어준다.
  • 현재 상태를 나타내는 state값과 이 상태를 변경하는 setState값을 한 쌍으로 제공한다.
  • 이 함수에 파라미터를 넣어서 호출하면 전달받은 파라미터로 값이 바뀌고 컴포넌트가 정상적으로 리렌더링된다.
  • state는 초기값을 설정할 수 있으며, 초기값은 첫 렌더링 때 한번 사용된다

2. useEffect

useEffect는 리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 훅이다.(외부 시스템과 컴포넌트를 동기화)

useEffect(setup, dependencies?)
  • setup에는 실행시킬 함수를 넣고 dependencies는 선택사항으로 의존성 배열을 담는다.
  • 기존 클래스형 컴포넌트에서 사용했던, componentDidMount, componentDidupdatea, componentWillunmount를 하나의 API로 통합한 것
  • 설정한 함수를 컴포넌트가 화면에 맨 처음 렌더링될 때만 실행하고, 업데이트될 때는 실행하지 않으려면 함수의 두 번째 파라미터로 비어 있는 배열을 넣어주면 된다.
useEffect(() => {
  console.log('마운트될 때만 실행됩니다.');
}, []);
  • 특정 값이 업데이트될 때만 실행하고 싶을때는 두 번째 파라미터로 전달되는 배열 안에 검사하고 싶은 값을 넣어주면 된다.
useEffect(() => {
  console.log(name);
}, [name]);

이 외에도 컴포넌트가 언마운트되기 전이나 업데이트되기 직전에 어떠한 작업을 수행하고 싶다면 useEffect에서 뒷정리(cleanup) 함수를 반환해 주어야한다.

useEffect(() => {
  console.log('effect');
  console.log(name);
  return() => {
    console.log('cleanup');
  	console.log(name);
  }
});

렌더링될 때마다 뒷정리 함수가 계속 나타나고, 뒷정리 함수가 호출될 때는 업데이트 되기 직전의 값을 보여준다.

언마운트 될 때만 뒷정리 함수를 호출하고 싶다면 useEffect 함수의 두 번째 파라미터에 비어있는 배열을 넣으면 된다.

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

3. useReducer

useReducer는 컴포넌트에 reducer를 추가하는 훅으로 useState보다 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트해 주고 싶을때 사용하는 훅이다.

const [state, dispatch] = useReducer(reducer, initialArg, init?)
  • reducer : 현재 상태, 그리고 업데이트를 위해 필요한 정보를 담은 액션(action) 값을 전달받아 새로운 상태를 반환하는 함수
  • initialArg : 초기 state가 계산되는 값
  • init : 선택사항, 초기 state를 반환하는 초기화 함수
import { useReducer } from 'react';

function reducer(state, action){
  //...
  // 불변성을 지키면서 업데이트한 새로운 상태를 반환한다. 
}

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, {age : 42});
  //...
  // 다른 값들이 필요하다면 추가로 들어감
}

액션값은 주로 다음과 같은 형태로 이루어져있다.

import React, { useReducer } from "react";
function reducer(state, action) {
  //action.type에 따라 다른 작업 수행
  // eslint-disable-next-line default-case
  switch (action.type) {
    case "INCREMENT":
      return { value: state.value + 1 };
    case "DECREMENT":
      return { value: state.value - 1 };
    default:
      return state;
  }
}
export default function Counter() {
  const [state, dispatch] = useReducer(reducer, { value: 0 });
  return (
    <div>
      <p>
        현재 카운터의 값은 <b>{state.value}</b>
        입니다.
      </p>
      <button onClick={() => dispatch({ type: "INCREMENT" })}>+1</button>
      <button onClick={() => dispatch({ type: "DECREMENT" })}>-1</button>
    </div>
  );
}
  • useReducer 을 사용했을 때의 가장 큰 장점은 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있다는 점 이다.

4. useMemo

useMemo는 재렌더링 사이에 계산 결과를 캐싱할 수 있게 해주는 훅으로 함수형 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있는 훅이다.

const cachedValue = useMemo(calculateValue, dependencies)
  • calculateValue : 캐싱하려는 값을 계산하는 함수
  • dependencies : calculateValue 코드 내에서 참소된 모든 반응형 값들의 목록
  • props로 전달받은 함수를 실행해서, 그 결과 값을 보존 (deps 의존인자가 하나라도 변하면 함수를 다시 실행해서 그 결과값을 보존)
import React, { useMemo, useState } from "react";

export default function Average() {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState("");

  const onChange = (e) => {
    setNumber(e.target.value);
  };
  const onInsert = (e) => {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber("");
  };

  const avg = useMemo(() => getAverage(list), [list]);
  return (
    <div>
      <input type="text" value={number} onChange={onChange} />
      <button onClick={onInsert}>등록</button>
      <ul>
        {list.map((value, index) => (
          <li key={index}>{value}</li>
        ))}
      </ul>
      <div>
        <b>평균값 : </b> {avg}
      </div>
    </div>
  );
}

const getAverage = (numbers) => {
  console.log("평균값 계산 중...");
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};
  • list 배열의 내용이 바뀔 때만 getAverage 함수가 호출된다.
  • 중복된 연산을 피할 수 있어 애플리케이션의 성능을 최적화할 수 있다는 장점이 있다.

5. useCallback

useCallback은 리렌더링 간에 함수 정의를 캐싱해주는 훅으로, 만들어 놨던 함수를 재사용할 수 있는 훅이다.

const cachedFn = useCallback(fn, dependencies)
  • 주로 렌더링 성능을 최적화해야 하는 상황해서 사용한다.
  • fn: 캐싱할 함숫값, 생성하고 싶은 함수
  • dependencies : fn 내에서 참조되는 모든 반응형 값의 목록, 배열(어떤 값이 바뀌었을 때 함수를 새로 생성해야 하는지 명시)
  • deps가 하나라도 변하면 그에 맞는 함수를 리턴
import React, { useCallback, useMemo, useState } from "react";

export default function Average() {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState("");

  const onChange = useCallback((e) => {
    setNumber(e.target.value);
  }, []); //컴포넌트가 처음 렌더링될 때만 함수 생성

  const onInsert = useCallback(
    (e) => {
      const nextList = list.concat(parseInt(number));
      setList(nextList);
      setNumber("");
    },
    [number, list]
  ); //number 혹은 list가 바뀌었을때 만 함수 생성

  const avg = useMemo(() => getAverage(list), [list]);
  return (
    <div>
      <input type="text" value={number} onChange={onChange} />
      <button onClick={onInsert}>등록</button>
      <ul>
        {list.map((value, index) => (
          <li key={index}>{value}</li>
        ))}
      </ul>
      <div>
        <b>평균값 : </b> {avg}
      </div>
    </div>
  );
}

const getAverage = (numbers) => {
  console.log("평균값 계산 중...");
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};
  • 중복된 연산을 피할 수 있어 애플리케이션의 성능을 최적화할 수 있다는 장점이 있다.

useCallback 은 결국 useMemo 에서 함수를 반환하는 상황에서 더 편하게 사용 할 수 있는 훅이다. 숫자, 문자열, 객체 처럼 일반 값을 재사용하기 위해서는 useMemo 를, 그리고 함수를 재사용 하기 위해서는 useCallback 을 사용하면된다.

6. useRef

useRef는 렌더링에 필요하지 않은 값을 참고할 수 있는 훅으로, 특정 DOM에 접근하여 DOM 조작을 가능하게 하는 훅이다.

const ref = useRef(initialValue)
import React, { useCallback, useMemo, useRef, useState } from "react";

export default function Average() {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState("");
  const inputEl = useRef(null);

  const onChange = useCallback((e) => {
    setNumber(e.target.value);
  }, []); //컴포넌트가 처음 렌더링될 때만 함수 생성

  const onInsert = useCallback(
    (e) => {
      const nextList = list.concat(parseInt(number));
      setList(nextList);
      setNumber("");
      inputEl.current.focus();
    },
    [number, list]
  ); //number 혹은 list가 바뀌었을때 만 함수 생성

  const avg = useMemo(() => getAverage(list), [list]);
  return (
    <div>
      <input type="text" value={number} onChange={onChange} ref={inputEl} />
      <button onClick={onInsert}>등록</button>
      <ul>
        {list.map((value, index) => (
          <li key={index}>{value}</li>
        ))}
      </ul>
      <div>
        <b>평균값 : </b> {avg}
      </div>
    </div>
  );
}

const getAverage = (numbers) => {
  console.log("평균값 계산 중...");
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};
  • useRef를 사용하여 ref를 설정하면 useRef를 통해 만든 객체 안의 current 값이 실제 엘리먼트를 가리킨다.
  • 특정 요소를 선택해야 할때 사용한다.

Hook은(함수들은) 값은 재사용이 아니라 로직의 재사용을 위한 것이다!

[ 출처 : https://ko.react.dev/reference/rules/rules-of-hooks ][ 출처 : 리액트를 다루는 기술 - 김민준(velopert) ]

0개의 댓글