오늘은 React 숙련주차에 접어들어 새로 익힌 리액트의 주요 hook 들에 대해 정리해보도록 하겠다.
useState
state
와 state
를 변경하는 업데이트 함수를 리턴하는 함수로, React hook 중에서 가장 기본적이고, 가장 많이 사용하는 hook이다.
const [state, setState] = useState(초기값);
// value === 변경될 state값
setState(value); // 일반 업데이트
//or
setState((prevState) => value) // 함수형 업데이트
위와 같이 사용하는게 기본으로, setState
함수를 호출해 state
를 변경하면 해당 state
가 선언된 상위 컴포넌트나 props
로 받는 하위 컴포넌트가 리렌더링된다. 이 때 state
를 변경하는 방법에는 일반적인 방법과 함수형 업데이트의 두 방법이 있는데, 함수형 업데이트를 간단히 짚어보자.
state
의 함수형 업데이트일반적인
state
업데이트는 배치(batch)형식으로 업데이트 된다.
배치 업데이트란 리액트가 업데이트 될 state
를 모아서 한 번에 업데이트 하는 것을 말한다. 아래의 예를 보자.
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
위와 같이 state
업데이트를 하는 경우, 세 번 업데이트 될 것 같지만 배치 업데이트로 인해 count
는 1 만 증가한다. 다음으로는 함수형 업데이트의 예를 보자.
setCount((prevCount) => prevCount + 1);
setCount((prevCount) => prevCount + 1);
setCount((prevCount) => prevCount + 1);
함수형 업데이트를 위와 같이 세 번 호출하면, 그 호출을 모아 순차적으로 실행시킨다. 리액트가 이런 식으로 상태 업데이트를 처리하는 이유는 불필요한 리렌더링을 줄여 성능을 높이기 위해서이다.
useEffect
dependencies array
내의 Reactive value
가 변경될때마다 callback 함수 내의 내용을 실행하는 훅이다. 바꿔 말해
렌더링 시에 특정 작업을 수행해야 할 때 사용하는 훅이다.
function UseEffect() {
const [value, setValue] = useState("");
useEffect(() => {
console.log("hello useEffect!");
}, []);
위의 코드에서는 콘솔 창에 로그를 띄워주는데, 이 효과는 페이지를 처음으로 로드할 때에만 일어난다. 왜일까?
dependencies array
useEffect(()=>{}, []);
useEffect
호출부에서 두 번째 인자를 보면 배열이 하나 있는데, 이 배열을 dependencies array
라 한다. 이 배열의 내용에 따라 useEffect
내부 함수의 실행 시기가 달라진다. 배열 안에는 Reactive value
를 담을 수 있는데, Reactive value
는 비단 props
나 state
뿐 아니라 이들을 이용해 계산한 값 또한 포함된다. 어찌 보면 당연한데, props
나 state
가 변해서 리렌더링이 일어나므로 계산한 값 또한 달라지게 되는 것이다. 배열의 내용에 따른 차이를 간단히 살펴보자.
Reactive value
가 업데이트 될 때마다useEffect
내부 함수가 호출된다.Reactive value
가 없다. 즉 내부 함수를 호출할 방법이 없는 것이다. 이런 경우 내부 함수는 컴포넌트가 처음 mount
될 때에만 호출된다.useEffect
훅에는 컴포넌트가 마운트 되고 리렌더링 될 때마다 실행되는 함수도 있지만 반대로 컴포넌트가 unmount 될 때 실행하는 함수도 정의할 수 있다. 이걸 cleanup function이라 하는데, 이 함수에 대해서는 아직 배운 바가 별로 없어 다음에 다뤄보도록 하겠다.
지금까지 살펴본 hook들은 실제로 리액트 앱이 원하는 대로 동작하는 데에 필수적인 것들이다. 물론 더 많이 있겠지만 지금까지 배운 내용들에서 분류를 하자면 그렇게 나눌 수 있을 것 같다는 이야기다.
이 다음부터 설명할 hook 들은 리액트로 다양한 층위의 컴포넌트를 구성하고 사용하는데 도움이 될 것들이다.
useRef
이 hook은 리액트 hook 의 네이밍 패턴인 use
어떤 값을 reference로 삼는다는 의미에서 Ref
를 붙인 hook으로, 용도는 크게 두 가지로 나눌 수 있다.
기본적인 용법은 아래와 같다
// 값 저장
const ref = useRef("초기값");
console.log(ref); // {current: '초기값'}
ref.current = "변경값";
console.log(ref); // {current: '변경값'}
// DOM 요소 접근
const idRef = useRef(null);
useEffect (() => {
idRef.current.focus(); // 컴포넌트가 마운트 될 때 idRef가 참조하는 DOM 요소에 `focus`
}, [])
return (
<>
<div>
아이디 : <input type="text" ref={idRef} /> <!-- ref 속성에 idRef를 전달 -->
</div>
</>
);
document.querySelector('#id')
와 같이 번거롭게 접근하지 않아도 된다는 점도 좋지만, 이 외에도 useRef hook 을 사용하는 장점은 더 있다.
state
와 비교했을 때 값을 저장한다는 점은 같지만 state
는 변화가 일어나면 다시 렌더링이 일어나고 컴포넌트 그 과정에서 내부 변수도 초기화 된다.useContext
props drilling으로 발생하는 문제에 대처하기 위한 대책으로 만들어진 hook이다. props drilling이란 상위 컴포넌트에서 계층 차이가 많이 나는 하위 컴포넌트가 필요로 하는 변수를 props 로 전달하기 위해 계속해서 하위 컴포넌트로 같은 props 를 전달하는 현상을 일컫는데, 이런 방식의 문제점이 뭘까?
이런 배경에서 useContext
가 등장한 것이다. 이 hook 의 경우 사용 과정에서 여러 파일을 오락가락 해야하므로 순서로 코드를 대신하겠다.
createContext
함수를 호출하고context
에 담을 데이터를 가진 컴포넌트에서 해당 js 파일을 import 하고MyComponentName.Provider
라는 React Element로 하위 컴포넌트를 감싸고value
속성에 object 형태로 변수를 넣어준다.useContext(MyComponentName)
의 값을 새로운 식별자myData
에 할당하고myData.propertyName
을 통해 접근할 수 있다.context
를 설정한 상위 컴포넌트에서 context
에 포함되어있는 데이터의 값을 바꾸면 해당 컴포넌트의 하위 컴포넌트가 모두 다 리렌더링된다. 후속 글이 나온다면 그 때에는 프로젝트 진행 과정에서 익힌 다른 hook 들을 다뤄보겠다.