import React, { useState } from 'react';
const [stageIdx, setStageIdx] = useState<number>(0);
const [questionIdx, setQuestionIdx] = useState<number>(1);
setQuestionIdx(questionIdx + 1);
setStageIdx(questionIdx);
useState를 사용할 때는 상태의 기본값을 파라미터로 넣어서 호출해준다. 이 함수를 호출해주면 배열이 반환되는데, 첫번째 원소는 현재 상태, 두번째 원소는 Setter함수이다.
배열 비구조화 할당을 통하여 다음과 같이 사용하지 않고 각 원소를 추출해서 사용한다.
const numberState = useState(0);
const number = numberState[0];
const setNumber = numberState[1];
const [number, setNumber] = useState(0);
const onIncrease = () => {
setNumber(prevNumber => prevNumber + 1);
}
const onDecrease = () => {
setNumber(prevNumber => prevNumber - 1);
}
Javascript를 사용할 때 특정 DOM 을 선택해야 하는 상황에 getElementById. querySelector 같은 DOM Selector 함수를 사용해서 DOM을 선택하는데, 리액트를 사용하는 프로젝트에서도 가끔 DOM을 직접 선택해야 하는 상황이 발생하면 ref 라는 것을 사용한다.
함수형 컴포넌트에서 ref를 사용할 때는 useRef라는 Hook 함수를 사용한다. (클래스형 컴포넌트에서는 콜백 함수를 사용하거나, React.createRef 라는 함수를 사용한다.)
const Result: React.FC<RouteComponentProps> = (props) => {
const element = useRef<HTMLDivElement>(null);
useEffect(() => {
if (element.current) {
const { current } = element;
current.style.transition = `transform 1.2s ease-in`;
current.style.transform = `translateX(1800px)`;
setTimeout(() => props.history.push("/result/bali"), 1500);
}
}, [props.history]);
return (
<MainWrapper>
<AnimationWrapper ref={element}>
<LoadingImage src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEQ...."/>
</AnimationWrapper>
</MainWrapper>
);
};
useRef()를 사용하여 Ref 객체를 만들고, 이 객체를 선택하고 싶은 DOM에 ref 값으로 설정해준다. 그러면 Ref 객체의 .current 값은 우리가 원하는 DOM 을 가르키게 된다.
useRef Hook은 DOM을 선택하는 용도 외에도 다른 용도가 한가지 더 있는데, 컴포넌트 안에서 조회 및 수정할 수 있는 변수를 관리하는 것이다.
useRef로 관리하는 변수는 값이 바뀐다고 해서 컴포넌트가 리렌더링 되지 않는다. 리액트 컴포넌트에서의 상태는 상태를 바꾸는 함수를 호출하고 나서 그 다음 렌더링 이후로 업데이트 된 상태를 조회할 수 있는 반면, useRef로 관리하고 있는 변수는 설정 후 바로 조회할 수 있다.
이 변수를 이용하여 setTimeout, setInterval 을 통해서 만들어진 id,
외부 라이브러리를 사용하여 생성된 인스턴스, scroll 위치 등의 값을 관리할 수 있다.
import React, { useRef } from 'react';
import UserList from './UserList';
function App() {
const users = [
{
id: 1,
username: 'test01',
email: 'test01@gmail.com'
},
{
id: 2,
username: 'test02',
email: 'test02@gmail.com'
},
{
id: 3,
username: 'test03',
email: 'test03@gmail.com'
}
];
const nextId = useRef(4);
const onCreate = () => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
return <UserList users={users} />;
}
export default App;
useEffect(() => {
console.log('컴포넌트가 화면에 나타남');
return () => {
console.log('컴포넌트가 화면에서 사라짐');
};
}, []);
useEffect를 사용할 때에는 첫번쨰 파라미터에는 함수, 두번째 파라미터에는 의존값이 들어있는 배열(dependency)을 넣는다. 만약 배열을 비우게 되면, 컴포넌트가 처음 나타날때에만 useEffect에 등록한 함수가 호출괸다.
그리고 useEffect에서 함수를 반환할 수 있는데, 이를 cleanup 함수라고 부른다. cleanup 함수는 useEffect에 대한 뒷정리를 해주는데, 배열이 비어있는 경우에는 컴포넌트가 사라질 때 cleanup 함수가 호출된다.
주로 마운트시에는 props 로 받은 값을 컴포넌트의 로컬 상태로 설정, 외부 API 요청청, setInterval 을 통한 반복작업 혹은 setTimeout 을 통한 작업 예약과 같은 작업을 하고, 언마운트시에는 setInterval, setTimeout 을 사용하여 등록한 작업들 clear 하기 (clearInterval, clearTimeout), 라이브러리 인스턴스 제거를 한다.
dependency에 특정 값을 넣으면 컴포넌트가 처음 마운트 될 때에도 호출이 되고, 지정한 값이 바뀔때에도 호출이 된다. 그리고 배열 안에 특정값이 있다면 언마운트시에도 호출이 되고 값이 바뀌기 직전에도 호출이 된다.
useEffect안에서 사용하는 상태나 props가 있다면 배열 안에 넣어주어야 한다. 만약 넣지 않는다면 useEffect에 등록한 함수가 실행될 때 최신 props와 상태를 가르키지 않게 된다.
배열을 아예 생략하게 된다면 컴포넌트가 리렌더링 될때마다 호출이 된다.
리액트 컴포넌트는 기본적으로 부모 컴포넌트가 리렌더링 되면 바뀐 내용이 없어도 자식 컴포넌트도 리렌더링된다.(실제 DOM 에 변화가 반영되는 것은 바뀐 내용이 있는 컴포넌트에만 해당하지만, Virtual DOM 에는 모든걸 다 렌더링하고 있다.)
벨로퍼트와 함께하는 모던 리액트
리액트의 Hooks 완벽 정복하기
About React Hook
When to useMemo and useCallback 영어 문서
때늦은 React Hooks 시리즈 4탄 - useCallback/useRef
When to useMemo and useCallback 한글 문서