TIL-56 다시 공부하는 React Hook

PRB·2021년 11월 21일
0

React

목록 보기
15/22
post-thumbnail

훅은 함수형 컴포넌트에 기능을 추가할 때 사용하는 함수이다.
훅을 사용하면 함수형 컴포넌트에서 상탯값을 사용할 수 있고, 자식 요소에 접근할 수도 있다. 비교적 최근에 추가된 기능이며 기존에 리액트가 가지고 있던 여러가지 문제를 해결해준다. 새로 작성하는 컴포넌트는 되도록 클래스형 컴포넌트보다는 훅을 사용해서 함수형 컴포넌트로 작성하는게 좋다.

1. 상태값 추가하기(useState)

useState 훅을 이용하면 컴포넌트에 상탯값을 추가할 수 있다.
useState 훅이 반환하는 배열의 두 번째 원소는 상탯값 변경 함수이다.
리액트는 상탯값 변경 함수가 호출되면 해당 컴포넌트를 다시 그린다. 그 과정에서 자식 컴포넌트도 같이 렌더링 된다.
리액트는 가능하다면 상탯값 변경을 배치(batch)로 처리한다. 다음은 상탯값 변경 함수를 연속해서 호출하는 코드이다.

배칭은 React가 더 나은 성능을 위해 여러 개의 state 업데이트를 하나의 리렌더링 (re-render)로 묶는 것을 의미한다.

function MyComponent() {
	const [count, setCount] = useState({ value : 0 });	
	function onClick() {
      setCount({value: count.value +1})
      setCount({value: count.value +1})
	}
  console.log('render called')
  return (
    <div>
      <h2>{count.value}</h2>
      <button onClick={onClick}>증가</button>
   	</div>
  );
}

count.value 상탯값을 두 번 증가시키려고 했다. 하지만 의도와 달리 1만큼만 증가한다. 이는 상탯값 변경 함수가 비동기로 동작하기 때문이다.
리액트는 효율적인 렌더링을 위해 여러 개의 상탯값 변경 요청을 배치로 처리한다. 따라서 onClick 함수가 호출되어도 로그는 한 번만 호출한다.

위에서 생긴 문제를 해결하기 위해 다음과 같이 상탯값 변경 함수의 인수로 함수를 입력할 수 있다.

function MyComponent() {
	const [count, setCount] = useState(0);	
	function onClick() {
      setCount(prev => prev+1);
      setCount(prev => prev+1);
	}
	// 

상탯값 변경 함수로 입력된 함수는 자신이 호출되기 직전의 상탯값을 매개변수로 받는다. 이 코드에서는 첫 번째 호출에서 변경된 상탯값이 두 번째 호출의 인수로 사용된다. 따라서 onClick 함수를 호출하면 count 상탯값은 2만큼 증가한다.

또한 상탯값 변경 함수는 비동기로 처리되지만 그 순서가 보장된다.

상탯값 변경 함수는 클래스 형 컴포넌트의 setState 메서드와 조금 다르게 동작한다. setState 메서드는 기존 상탯값과 새로 입력된 값을 병합하지만 useState 훅의 상탯값 변경 함수는 이전 상탯값을 덮어쓴다.

두 상탯값을 하나의 객체로 관리할 때 ... state와 같은 코드가 필요하다
이렇게 상탯값을 하나의 객체로 관리할 때는 useReducr 훅을 사용하는 게 좋다

2. 부수 효과 처리하기(useEffect)

함수 실행 시 함수 외부의 상태를 변경하는 연상을 부수효과라고 부른다.
특별한 이유가 없다면 모든 부수 효과는 useEffect 훅에서 처리하는 게 좋다.

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

function MyComponent() {
  const [count, setCount] = useState(0);
  useEffect(()=>{
  	document.title = `업데이트 횟수: ${count}`;
  });
  return <button onClick={() => setCount(count + 1)}>increase</button>;

useEffect 훅에 입력하는 함수를 부수 효과 함수라고 한다.
부수효과 함수는 렌더링 결과가 실제 돔에 반영된 후 호출되고, 컴포넌트가 사라지기 직전에 마지막에 호출된다. 버튼을 클릭할 때마다 다시 렌더링 되고, 렌더링이 끝나면 부수효과 함수가 호출된다.

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

function WidthPrinter() {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(()=>{
  	const onResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', onResize);
    return () => {
    window.removeEventListener('resize', onResize);
    }
  }, []);
  return <div>{`width is ${width}`}</div>;
}

창 크기가 변경될 때마다 onResize 이벤트 처리 함수가 호출되도록 등록한다.
부수효과 함수는 함수를 반환할 수 있다. 반환된 함수는 부수효과 함수가 호출되기 직전에 호출되고, 컴포넌트가 사라지기 직전에 마지막으로 호출된다. 따라서 부수 효과 함수가 반환한 함수는 프로그램이 비정상적으로 종료되지 않는다면 반드시 호출될 것이 보장된다. 의존성 배열로 빈 배열을 입력하면 컴포넌트가 생성될 때만 부수효과 함수가 호출되고, 컴포넌트가 사라질 때만 반환된 함수가 호출된다.

3. 훅 만들기(custom hook)

리액트가 제공하는 훅을 이용해서 커스텀 훅을 만들 수 있다.
리액트의 내장 훅처럼 커스텀 훅을 이름은 use로 시작하는게 좋다. 그러면 코드 가독성이 좋아지고, 여러 리액트 개발 도구의 도움도 쉽게 받을 수 있다.

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

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(()=>{
    const onResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', onResize);
    return () => {
      window.removeEventListener('resize', onResize);
    };
  }, []);
  return width;
}
  function WidthPinter() {
    const width = useWindowWidth();
    return <div>{`width is ${width}`}</div>;
  }

useWindowWidth 훅은 창의 너비를 저장해 두고 필요할 때마다 값을 제공한다.
창의 너비가 변경되면 새로운 창의 너비로 다시 렌더링된다.

4. 훅 사용 규칙

훅을 사용할 때는 다음 규칙을 지켜야 한다.

  1. 하나의 컴포넌트에서 훅을 호출하는 순서는 항상 같아야 한다.
    Ex) 조건문,반복문,함수 안에서 useState 사용하면 안된다.
  2. 훅은 함수형 컴포넌트 또는 커스텀 훅 안에서만 호출되어야 한다.

이 두 규칙을 지켜야 리액트가 각 훅의 상태를 제대로 기억할 수 있다.

function Profile() {
  const [age, setAge] = useState(0);
  const [name, setName] = useState('');
  // ...
  useEffect(() => {
  	// ...
    setAge(23);
  }, []);
  // ...
}

우리가 useState 훅에 전달한 정보는 상탯값의 기본값밖에 없다. 리액트가 age와 name 상탯값을 구분할 수 있는 유일한 정보는 훅이 사용된 순서이다.
1. age 상탯값이 23으로 변경됐다. 리액트는 첫 번째 훅의 상탯값을 23으로 설정한다. 만약 조건문에 의해 실행되지 않는다면 name의 값은 23이 되므로 문제가 된다.

5. 훅을 처리하는 방식

let hooks = null;

export function useHook() {
  // ...
  hooks.push(hooksData);
}

function process_a_component_rendering(component) {
  hooks = [];
  component();
  let hooksForThisComponent = hooks;
  hooks = null;
  // ...

리액트가 내장하고 있는 useState, useEeffect와 같은 훅이다.
각 훅 함수에서는 hooks 배열에 자신의 데이터를 추가한다.
렌더링 과정에서 하나의 컴포넌트를 처리하는 함수이다.
hooks를 빈 배열로 초기화한다. 컴포넌트 내부에서 훅을 사용한 만큼 hooks 배열에 데이터가 추가된다.
생성된 배열을 저장하고 hooks 변수를 초기화한다.
이처럼 리액트는 훅이 사용된 순서를 저장하고 배열에 저장된 순서를 기반으로 훅을 관리한다.

profile
사용자 입장에서 사용자가 원하는 것을 개발하는 프론트엔드 개발자입니다.

0개의 댓글