목표
react hook
공부
react를 공부하던 도중, Hook 이라는 개념이 등장했다.
이 Hook이란 뭘까?
컴포넌트
컴포넌트에는 함수 컴포넌트와 클래스 컴포넌트가 있다.
클래스 컴포넌트의 경우
- state 정의
- setState() 함수를 통한 state 업데이트
- Lifecycle methods 제공
같은 것을 지원하지만
함수 컴포넌트의 경우
- state 사용 불가
- Lifecycle에 따른 기능 구현 불가
정도의 차이가 존재한다.
Hook?
클래스 컴포넌트와 함수 컴포넌트의 차이를 봤을 때, 함수 컴포넌트에서는 왜 사용을 못하지? 싶은 것들이 많다.
react 버전16 이후에 나온 Hook에선 함수 컴포넌트를 클래스 컴포넌트처럼 사용할 수 있도록 해준다.
원하는 시점에 정해진 함수를 실행할 수 있도록 만든 것이고,
이러한 함수들을 합쳐서 Hooks라고 한다.
custom hook은 이름을 자유롭게 지을 수 있지만,
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>
);
}이 함수를 보자, 그냥 버튼을 누르면 카운트를 증가시키고 현재 카운트를 보여주는 함수이다.
정상적으로 동작할까?
물론 클릭 자체는 동작한다.
하지만 카운트를 함수의 변수로 선언하고 사용하면, 버튼 클릭 시에 재렌더링이 일어나지 않아 새로운 값으로 갱신이 되지 않는다.
이것을 해결하려면 클릭을 했을 때 렌더링이 일어나도록 하면 된다.
하지만 안타깝게도 함수 컴포넌트에서는 이를 지원하지 않는다.
Hook을 사용하자
이때, useState()를 사용하면 가능하다.
const [변수명, set함수명] = useState(초기값);useState는 이렇게 사용한다. 코드에 적용해보자
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>
);
}이렇게하면, 버튼이 클릭되면 setCount로 count 값을 1 증가시키고,
count 값이 변경되어 컴포넌트가 재렌더링이 된다.
그럼에도 불구하고, 클래스 컴포넌트와의 차이점이 있다.
클래스 컴포넌트는 setState() 함수 하나로 모든 state를 업데이트 할 수 있지만, 함수 컴포넌트는 변수 각각에 대해 set 함수가 따로 존재한다.
useEffect()
이 Hook은 Side effect를 수행하기 위한 Hook이다.
이 side effect란 뭘까? 리액트에서는 그저 효과, 영향 등으로 부작용의 의미보다는 effect의 의미를 가진다.
왜 effect의 의미를 가지는 것일까?
왜냐하면 이 작업들은 다른 컴포넌트에 영향을 미칠 수 있고,
렌더링 중에는 작업이 완료될 수 없기 때문이다.
이 작업들이 side에서 실행된다는 의미에서 side effect라고 부른다.
이러한 Side effect를 함수 컴포넌트에서 사용할 수 있게 하기 위해 useEffect()라는 Hook을 사용한다.
useEffect(이펙트 함수, 의존성 배열);이 의존성 배열이라는 것이 생소할텐데,
해당 이펙트가 의존하고 있는 배열이다. 배열 내의 변수중에 하나라도 변경되었을 때에 이펙트 함수가 실행된다.
의존성 배열을 생략하고 사용할 수도 있는데
useEffect(이펙트 함수);이렇게 하면 컴포넌트가 업데이트 될 때마다 이펙트 함수가 실행된다.
import React, { useState, useEffect } from "react";
function Counter(props) {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `${count}`;
});
return(
<div>
<p> {count} </p>
<button onClick={() => setCount(count + 1)}>
클릭버튼
</button>
</div>
);
}처음에 보고 useEffect 사용법이랑 다른 것 같았는데,
해당 useEffect는 의존성 배열이 없다.
즉, 컴포넌트가 업데이트 될 때마다 실행된다. 실행되는 함수가 뭘까?
() => {
document.title = `${count}`;
}이 함수가 실행되는 것이다.
좀 더 자세하게 말하면 결국, useEffect는 여기에서 componentDidMount, componentDidUpdate와 비슷하게 작동한다고 볼 수 있다.
그럼 componentWillUnmount와 비슷하게 작동하려면 어떻게 해야할까?
바로 이펙트 함수에 return함수를 추가하면 된다.
이렇게 하면 컴포넌트가 unmount될 때 호출이 된다.
정리하자면,
useEffect(() => {
// 컴포넌트가 마운트 된 이후 의존성 배열 변수중 하나라도 값이 변경되면 실행
// 빈 의존성 배열을 넣으면 mount와 unmount시에 한 번씩만 실행
// 의존성 배열을 생략하면 컴포넌트 업데이트 때마다 실행
...
return () => {
// 컴포넌트가 unmount되기 전에 실행
...
}
}, 의존성 배열);이런 의미를 가진다.
useMemo
이 Hook은 memoized value를 생성하는 역할을 하는데,
memoized value란, 연산량이 높은 작업을 수행한 뒤에 저장해두고 그 작업이 다음에 호출되었을 때에는 저장된 값을 반환하여 작업 속도를 높인다.
해당 작업을 저장하는 변수가 memoized value이다.
const memoizedValue = useMemo(
() => {
// 연산량이 높은 작업 수행 후 반환
return computeExpensiveValue(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);이렇게 사용한다.
이렇게 해둔다면, 컴포넌트가 재렌더링 될 때마다 연산량이 높은 작업을 반복하지 않도록 할 수 있다.
즉, 빠른 렌더링 속도를 보장한다.
만약 의존성 배열을 추가하지 않는다면, 아무 의미가 없어진다
-> 매 렌더링마다 실행된다.
빈 의존성 배열을 넣는다면, 컴포넌트가 mount 될 때만 실행된다.
useCallback
useMemo와 비슷한데, 의존성 배열의 값이 바뀔 때만
함수
를 다시 정의해서 반환한다.
const memoizedCallback = useCallback(
() => {
dosomething(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);이렇게 사용한다.
저장된 값을 반환한다는 점에서 useMemo와 완전히 동일하다.
그럼 똑같이 사용할 수도 있지 않을까?
-> 가능하다
useCallback(함수, 의존성 배열);
useMemo(() => 함수, 의존성 배열);이 두 함수는 정확히 동일한 역할을 한다.
useRef
Reference를 사용하기 위한 Hook이다.
Reference란, 특정 컴포넌트에 접근할 수 있는 객체를 의미한다.
<input ref={inputElem} type="text" />이렇게 작성하면 노트가 변경될 때마다 inputElem의 current 속성에 현재 해당되는 DOM 노드를 저장한다.
useRef Hook은 이 객체를 반환한다.
const refContainer = useRef(초기값);이렇게 선언하면 컴포넌트 해제 전까지 존재한다.
Hook 규칙
- Hookd은 무조건 최상위 레벨에서 호출해야 한다.(함수 컴포넌트의 최상위)
- React 함수 컴포넌트에서만 Hook을 호출해야 한다.(js에서는 x)