[리액트] Hooks

훅은 리액트 버전 16.8에서 새롭게 등장한 개념이다. 현재 대부분은 Hook를 사용하기 때문에 중요한 부분

Hook의 개념과 useState, useEffect

Hook

  • 리액트 컴포넌트는 클래스 컴포넌트와 함수 컴포넌트 두 가지가 존재한다.
  • 컴포넌트에는 state라는 중요한 개념을 갖고 있는데, 이 state를 이용하여 렌더링에 필요한 데이터를 관리한다. (붕어빵 틀 : 컴포넌트, 붕어빵 : react element, 붕어빵 속재료: state)
  • 클래스 컴포넌트에서는 생성자를 통해 state를 관리, setState() 함수를 통해 state 업데이트, LifeCycle methods를 제공하여 컴포넌트의 생명주기에 따른 기능 구현이 가능
  • 원래 함수 컴포넌트는 state를 사용불가 했으며, 컴포넌트 생명주기에 따른 기능 구현이 불가한 상태였다. 클래스 컴포넌트처럼 state와 LifeCycle 기능을 지원하기 위해 Hook이 등장했다.(훅을 사용하면 함수 컴포넌트도 클래스 컴포넌트의 기능을 모두 동일하게 구현이 가능하다.)

 

Hook은 갈고리라는 의미의 단어다. 프로그래밍에서는 원래 존재하는 어떤 기능에 마치 갈고리를 거는 것처럼 끼어 들어가 같이 수행하는 것

 

Hook은 앞에 use를 사용한다.개발자가 커스텀훅을 만든다면 Hook의 규칙에 따라서 use를 앞에 붙여야 한다.

 

useState

  • state를 사용하기 위한 Hook
import React, { useState } from "react";

function Counter(props) {
	var count = 0;
	
	return (
		<div>
			<p>총 {count}번 클릭했습니다.</p>
			<button onClick={() => count++}>
				클릭
			</button>
		</div>
	);
}
  • 기존 함수 컴포넌트는 state를 제공하지 않아서 위 코드처럼 버튼 클릭 시 count변수는 증가하지만 재렌더링이 되지 않아서 화면에 표시가 되진 않는다.

 

import React, { useState } from "react";

function Counter(props) {
	const [count, setCount] = useState(0);
	
	return (
		<div>
			<p>총 {count}번 클릭했습니다.</p>
			<button onClick={() => setCount(count+1)}>
				클릭
			</button>
		</div>
	);
}
  • useState를 통해 초기값을 넣어주면 배열을 반환해 준다. 배열은 state로 선언된 변수와 해당 state의 set함수가 들어있다.
  • 클래스 컴포넌트는 setState 함수 하나로 모든 state값을 업데이트 가능하지만 useState는 변수 각각에 대한 set 함수가 따로 존재한다.

 

useEffect

Side effect를 수행하기 위한 Hook이다.

여기서 Side effect는 사전적으로 부작용이라는 뜻을 갖고 있다. 일반적은 프로그래밍에서도 개발자가 의도치 않은 버그를 사이드이펙트라 부른다. 하지만 리액트에서는 효과, 영향의 의미를 갖고 있다.

  • (예시) 서버에서 데이터를 받거나, 수동으로 DOM을 변경하는 작업

 

useEffect는 클래스 컴포넌트의 생명 주기 함수들을 하나로 통합해서 제공한다.

useEffect(이펙트 함수, 의존성 변수 배열);
  • 의존성 배열 : 이펙트가 의존하고 있는 배열, 배열 안에 있는 변수가 하나라도 값이 변경되었을 때 이펙트 함수가 실행된다.
  • 이펙트 함수는 기본적으로 처음 렌더링 이후와 업데이트 재렌더링 이후에 실행
  • 의존성 변수 배열을 [ ] 빈 배열로 설정할 경우 이펙트 함수는 mount와 unmount 시에 한 번만 실행된다.
  • 이펙트 함수만 파라미터로 사용할 경우(의존성 변수 배열 생략) 컴포넌트가 업데이트될 때마다 이펙트 함수가 호출

 

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

function Counter(props) {
	const [count, setCount] = useState(0);

	useEffect(() => {
		document.title = `You clicked ${count} times`;
	});

	return (
		<div>
			<p>총 {count}번 클릭했습니다.</p>
			<button onClick={() => setCount(count+1)}>
				클릭
			</button>
		</div>
	);
}
  • 버튼이 클릭 될 때마다 컴포넌트가 재 렌더링 -> 재 렌더링 후에 useEffect 함수가 실행된다.(document.title 업데이트)
  • 의존성 변수 배열 부분을 생략했으므로 해당 useEffect는 매 렌더링 시 함수 실행
  • 클래스 컴포넌트의 componentDidMount, componentDidUpdate 함수 기능을 위 useEffect가 하고 있다.

 

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

function UserStatus(props) {
	const [isOnline, setIsOnline] = useState(null);

	function handleStatusChange(status) {
		setIsOnline(status.isOnline);
	}

	useEffect(() => {
		ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
		return () => {
			ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
		};
	});

	if (isOnline === null) {
		return '대기중...';
	}

	return isOnline ? '온라인' : '오프라인';
}
  • useEffect에서 return 함수는 클래스 컴포넌트의 생명주기 함수인 componentWillUnMount와 동일(언마운트 될 때 호출)
  • useEffect는 하나의 컴포넌트에 여러 개를 사용할 수 있다.

 

useMemo, useCallback, useRef

useMemo

  • memozied value를 리턴하는 hook

 

Memoization : 컴퓨터 프로그래밍에서 최적화를 위해 사용하는 기법, 비용이 높은(연산량이 많은) 함수 호출 결과를 저장해 놨다가 재사용하는 방법

 

useMemo 사용법

const memoizedValue = useMemo(
	() => {
		// 연상량이 높은 작업을 수행하여 결과를 반환
		return computeExpensiveValue(의존성 변수1, 의존성 변수2);
	},
	[의존성 변수1, 의존성 변수2]
);
  • 파라미터는 memoized value를 생성하는 함수와 의존성 변수 배열을 갖는다.
  • 의존성 변수 배열 안에 있는 변수가 변했을 때만 memoizedValue create함수가 실행되어 결과값을 반환한다. 그렇지 않을 경우 기존 결과값을 사용
  • useMemo는 렌더링 중에 실행되므로, 렌더링 중에 실행되면 안 되는 로직은 넣으면 안 된다. ( ex) side effect - useEffect에서 사용하는 함수)
  • 의존성 배열을 넣지 않을 경우 매번 렌더링이 진행되므로 useMemo를 쓰는 이유가 사라진다. 의존성 변수 배열 자리에 [ ] 빈 배열을 넣으면 컴포넌트가 처음 마운트 될 때만 함수가 실행된다. (useMemo훅은 대부분 의존성 변수 배열을 넣어서 사용한다.)

 

useCallback

  • useMemo 훅과 흡사하다. useMemo 훅은 memoizedValue라면 useCallback은 memoizedCallback이다.
const memoizedCallback = useCallback(
	() => {
		doSomething(의존성 변수1, 의존성 변수2);
	},
	[의존성 변수1, 의존성 변수2]
);
  • 파라미터로 받는 함수를 콜백함수라 하고, 의존성 변수 배열이 변경되면 해당 콜백 함수가 실행된다
  • useMemo와 마찬가지로 빈 배열인 경우 마운트 시 한 번만 렌더링 되며, 의존성 배열을 생략하면 매번 렌더링이 진행된다.

 

useCallback 사용하지 않고 컴포넌트 내 함수를 정의를 하면 렌더링이 일어날 때마다 함수가 정의된다. => 특정한 값이 변경될 때만 함수가 정의될 수 있도록 하자(불필요한 작업 줄이기)

useCallback을 사용하지 않는다면 부모컴포넌트의 함수가 자식 컴포넌트 props로 전달될 때 재렌더링 시 매번 함수가 정의되면서 자식컴포넌트도 같이 재 렌더링이 된다.

 

useRef

레퍼런스를 사용하기 위한 훅

레퍼런스란 특정 컴포넌트에 접근할 수 있는 객체
  • useRef 훅은 Reference를 반환한다.
  • refObject.current : 현재 참조하고 있는 Element
  • 사용법
const refContainer = useRef(초기값);
  • 파라미터에 초기값을 넣으면 해당 초기값으로 초기화된 레퍼런스 객체를 얻는다.
  • 초기값이 널이면 → null인 레퍼런스 객체가 반환
  • 컴포넌트의 라이프타임 전체에 걸쳐서 유지 ( 컴포넌트가 마운트 해제 전까지 유지 )
  • 변경 가능한 current라는 속성을 가진 하나의 상자

 

function TextInputWithFocusButton(props) {
	const inputElem = useRef(null);

	const onButtonClick = () => {
		// current는 마운트된 input element를 가리킴
		inputElem.current.focus();
	};

	return (
		<>
			<input ref={inputElem} type="text" />
			<button onClick={onButtonClick}>
				Focus the input
			</button>
		</>
	);
}
  • useRef 훅을 사용하여 버튼 클릭 시 인풋에 포커싱 되는 코드
  • 초기값은 null인 상태 inputElem이라는 객체를 input에 넣어준다.
  • 버튼 클릭 시 inputElem.current.focust(); 진행이 된다.
  • useRef 훅은 current 속성이 변경되어도 재렌더링이 진행되지 않는다.

 

훅의 규칙과 커스텀 훅 만들기

 

훅의 규칙 

  • 훅은 무조건 함수 컴포넌트의 최상위 레벨에서 호출해야 한다.
    • 반복문, 조건문에서 훅을 호출해서는 안된다.
    • 매번 같은 순서의 훅이 호출되어야 state관리가 잘된다.

 

커스텀 훅 만들기

커스텀 훅 : 이름이 use로 시작하고, 내부에 훅을 호출하는 자바스크립트 함수
  • 앞에 use를 붙여야 한다. , 훅들을 최상위 레벨에서 사용해야 한다.(훅의 규칙)
  • 여러 컴포넌트에서 중복해서 사용하는 훅들이 있다면 묶어서 커스텀 훅을 만들어서 사용할 수 있다.

 

 

인프런 처음 만난 리액트

 

[무료] 처음 만난 리액트(React) - 인프런 | 강의

자바스크립트와 CSS 기초 문법과 함께 리액트의 기초를 탄탄하게 다질 수 있습니다., 깔끔한 강의자료, 꼼꼼한 설명으로쉽게 배우는 리액트 강의입니다. 👨‍🏫 리액트의 세계로초대합니다 💫

www.inflearn.com