TIL25. hooks 개요 및 useState, useRef

imloopy·2022년 5월 26일
0

Today I Learned

목록 보기
28/56

hooks

hook은 리액트 16.8 버전부터 새롭게 출시된 기능으로, 클래스 컴포넌트 형식을 사용하지 않아도 state와 다른 리액트의 기능을 사용할 수 있다.

hook의 특징

  • 비즈니스 로직의 재사용 가능
    • hook은 클래스 컴포넌트에서 구현하기 어려웠던 계층의 변화 없이 상태 관련 로직을 재사용 할 수 있도록 도와준다.
  • 간단한 사이드이펙트 관리
    • 클래스 컴포넌트는 사이드 이펙트를 리액트의 라이프 사이클 메서드로 관리하였다.
    • 이는 각 생명주기 메서드에서 자주 관련 없는 로직이 포함되기도 하며, 이는 컴포넌트의 무결성을 해칠 수 있다.
  • 클래스 없이 리액트의 기능을 사용할 수 있다.
    • 자바스크립트의 this는 다른 언어의 this와 다르게 동작하므로, 자바스크립트에 익숙하지 않은 사용자들은 잘못된 코드를 짤 수 있다.

hook의 종류

  • useState
  • useRef
  • useEffect
  • useMemo
  • useCallback

useState

useState로 관리하는 변수의 값이 변경되면 컴포넌트의 재렌더링이 발생한다. 리액트 컴포넌트가 항상 최신의 상태를 반영하도록 도와준다.

function App(): JSX.Element {
	const [counter, setCounter] = useState(0);

  return (
    <div className="App">
			<button onClick={() => setCounter(prev => prev + 1)}>Click!</button>
			{/* useState로부터 반환받은 state가 갱신되면 자동으로 페이지가 갱신된다. */}
			{ counter }
    </div>
  );
}

export default App;

리액트에서 데이터의 흐름은 상위 컴포넌트에서 하위 컴포넌트 방향으로 흐른다. 만약 하위 컴포넌트의 변화가 상위 컴포넌트의 데이터에 영향을 미치게 하고 싶다면, 상위 컴포넌트의 useState로부터 반환받은 dispatch(setState) 함수를 prop으로 전달하는 방법을 사용할 수 있다.

// App.js
function App(): JSX.Element {
  const [totalCount, setTotalCount] = useState(0);
  const increaseTotalCount = () => setTotalCount((prev) => prev + 1);
  const decreaseTotalCount = () => setTotalCount((prev) => prev - 1);
  return (
    <div className="App">
      <h1>{totalCount}</h1>
			{/* 
				onIncrease, onDecrease 프롭에 메서드를 전달한다.
				setState 함수를 직접 전달하는 것을 지양한다.
			*/}
      <Counter
        onIncrease={increaseTotalCount}
        onDecrease={decreaseTotalCount}
      />
      <Counter
        onIncrease={increaseTotalCount}
        onDecrease={decreaseTotalCount}
      />
      <Counter
        onIncrease={increaseTotalCount}
        onDecrease={decreaseTotalCount}
      />
    </div>
  );
}

export default App;

redux를 이용한 전역 상태 관리, eventbus 사용 등 여러가지 방법이 있지만, 이 방법은 별도의 라이브러리 설치 없이 데이터를 다룰 수 있는 가장 간단한 방법이다.

useRef

useRef는 .current 프로퍼티로 전달된 초깃값(initialValue)로 초기화된 변경 가능한 객체(ref)를 반환한다.

  • DOM 객체를 직접 가리켜서 내부 값을 변경하거나 focus() 등의 dom method 들을 사용하고자 할 때 사용할 수 있다. 리액트에서 DOM으로 직접적인 접근을 제한하고 있으므로 ref를 통해서만 실제 DOM으로 접근할 수 있다.
  • useRef는 지역 변수로 사용하기 위하여 사용될 수 있지만, useState와 달리 값이 변경되더라도 재 렌더링이 발생하지 않는다. (데이터가 컴포넌트와 일치하지 않을 수 있다.)

타입스크립트에서 const inputRef = useRef() 형태로 사용하면 컴파일 에러가 발생한다! 이를 해결하기 위해서는 generic 정의가 필요하다.

  • useRef에 null이 아닌 다른 값을 넣는다면, current를 이용하여 로컬 변수로 활용될 수 있다.
    • useRef<T>(initialValue: T): MutableRefObject<T>
  • 인자 타입에 null을 허용하면 MutableRefObject가 아닌 RefObject 가 반환된다. → dom을 조작하는데 사용할 수 있다.
    • useRef<T>(initialValue: T|null): RefObject<T>
const inputRef = useRef<HTMLInputElement>(null);
// inputRef가 possibly null일 수 있으므로, type gurad를 통해 타입을 체크해야 한다.
const onClick = () => {
  if (inputRef.current) {
    inputRef.current.focus();
  }
};

useState와 useRef의 특징 정리하기

공통점

  • 함수형 컴포넌트에서 동적인 상태 관리 가능
    • useState의 경우 const [state, setState] = useState(initialValue)에서 setState로 상태를 변경, state는 변경된 상태를 반환받음
    • useRef의 경우 ref.current 에 값을 동적으로 할당

차이점

  • useRef는 useState와 달리 .current의 값이 변경되더라도 컴포넌트가 재 렌더링되지 않는다. 즉, 컴포넌트는 여전히 initialValue를 가리킨다.

느낀점

  • 타입스크립트 환경에서 리액트를 다루고 있는데, 리액트와 타입스크립트 모두 익숙하지 않다보니 확실히 이해하는데 걸리는 시간이 긴 것 같다.
  • 특히 useRef는 단순히 DOM에 연결해서 DOM을 조작하기 위하여 사용하는 것인줄 알았는데, mutable하게 관리할 수 있음을 알았고, 재렌더링시에도 값이 유지됨을 알았다. 다만, 타입을 빡세게 지정해주어야 해서 다소 애를 먹었다.. 꼭 useRef<Element>(null) 형식을 지켜줘야 타입 스크립트에서 잘 인식하는 것 같다.
  • useState는 그동안 많이 써보긴 했는데, useRef는 잘 써보진 않았다. setInterval 관리할 때에도 주로 useEffect를 사용했는데, useRef로 관리할 경우 렌더링 최적화에 더 유리하다고 한다. 관련 자료를 찾아서 다시 한번 정리해야겠다.

출처

TypeScript React에서 useRef의 3가지 정의와 각각의 적절한 사용법
useState vs useRef

0개의 댓글