-
Notifications
You must be signed in to change notification settings - Fork 2
기술 공유 react useState
컴포넌트 내부에서 특정 토픽의 데이터를 관리해주어야 하고, 해당 데이터가 바뀔 때마다 그에 맞게 재렌더링이 필요할 때, 이를 사용할 수 있다.
import {useState} from "react";
const Component = () => {
// in JS
const [state,setState] = useState(initValue);
// in TS
const [state,setState] = useState<typeOfState>(initValue);
return <></>;
};
useState
를 호출하면 이에 대한 반환으로 상태값과 해당 상태값을 변화시키는 함수를 반환한다. 따라서 보통 위와 같이 구조 분해 할당으로 받아준다.
아래는 개인적인 의문을 해결하기 위해 시도한 여러 실험의 결과이다.
-
useState
를 사용하지 않은 컴포넌트 내 변수?컴포넌트 내 변수를
state
로 관리해주지 않더라도 컴포넌트는 잘 작동한다. 다만, 후에 해당 값이 바뀌게 되더라도(ex. 사용자와의 상호작용을 통한 값의 변경) 브라우저 측 변수의 값은 바뀌어도 해당 값이 이용되어 화면의 상태가 변하지는 않는다.(재렌더링 되지 않음) -
컴포넌트 내 재렌더링의 기준?
컴포넌트가 재렌더링 될 때, 컴포넌트 함수의 코드 블록에 있는 모든 코드가 재 실행되는데(return 내 코드를 포함한 전부), 컴포넌트가 다시 그려지는 경우(재렌더링)는 크게 다음과 같다.
- 리액트 개발 모드를 사용할 경우(초기 렌더링된 후 자동으로 재렌더링 처리)
- 컴포넌트 내 상태가
setState
함수를 통해 변했을 때(만약 해당 상태가 렌더링에 사용되지 않더라도(화면에 출력되지 않더라도) 재렌더링됨) - 상위 컴포넌트가 재렌더링될 때(만약 해당 컴포넌트에서 사용되지 않는 상위 컴포넌트의 상태의 변화가 있을 때도 일괄적으로 재렌더링)
- 처음엔
props
값이 변할 때도 재렌더링되지 않을까 했는데, 그러진 않았음. 다만props
가 상위 컴포넌트의 상태로 관리될 경우, 상태가 변경되면 어차피 해당 클라이언트부터 주욱 재렌더링되기 때문에 그렇게 착각할 수 있음.
- 처음엔
-
재렌더링될 때
state
값은 왜 초기화되지 않는지?컴포넌트의
state
값은 React 내의state
배열의 원소로서 관리되고,useState
가 처음 선언될 때마다 그에 상응하는 커서를 생성하고 해당 커서를 인덱스 값으로 이용하여state
배열에서 값을 찾게 된다. 쉽게 말해state
값은 컴포넌트에 종속된 값이 아닌 react 모듈 내 렉시컬 스코프의 변수를 참조해서 가져오는 것이다.(클로저) 따라서 처음useState
가 사용되었을 때(초기 렌더링)는 값을 react 모듈 내 변수로 선언해주고, 이후 렌더링부터는 그 변수를 그대로 참조해서 가져오는 것이다. 즉useState
함수는 렌더링될 때마다 실행되지만 함수 내부적 처리를 통해 안그렇게 보이도록 설계되어있는 것이다. (참고:https://seokzin.tistory.com/entry/React-useState%EC%9D%98-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC%EC%99%80-%ED%81%B4%EB%A1%9C%EC%A0%80) -
setState
함수의 함수형 인자setState
의 인자는 값과 함수를 모두 받을 수 있다. 둘의 차이라고 하면, 만약 값으로 인자를 받을 경우, 상태가 변경되어 재렌더링이 이루어지고useState
를 통해서 바뀐 값을 다시 가져오고 나서야 바뀐 상태값을 이용할 수 있다. 만약 함수를 인자로 받을 경우엔setState
가 호출된 이후 곧바로 바뀐 값을 사용할 수 있게된다. 따라서useEffect
와 같은 훅에서 상태를 변경시킨 후 이 값을 바로 사용하기 위해서는 함수로 상태를 넘겨주어야 한다. -
연속된
setState
호출 시 가장 마지막 호출만 적용되는 현상이는
setState
인자에 값을 할당했을 때 나타나는 현상인데, 이것이 발생하는 이유는setState
가 비동기 방식으로 작동하기 때문이다.setState
가 수행하는 작업은state
값을 수정하고 재렌더링 시키는데, 만약 이것이 동기적으로 작동한다면 재렌더링이 굉장히 자주 발생하여 성능 상에 문제가 발생할 것이다. 따라서 리액트는 이를 비동기로 처리하는데, 이 때 연속된 변경 사항들을 하나로 통합하여 처리하게 된다. 이 때 만약 인자로 값을 주었다면 변경 사항들을 통합하는 과정에서 마지막 변경만 살아남게 된다. 이를 해결하기 위해서는 인자로 함수를 주면 된다. 이렇게 주더라도setState
가 호출된 후 콘솔을 찍어보면 그대로이지만(비동기로 처리되는 것은 동일하기 때문) 최소 다음 렌더링 때 연속된 변경 사항들이 모두 적용시킬 수 있다.import {useState} from "react"; const [count,setCount] = useState(0); // 값으로 state 변경 setCount(count+1); // 함수로 state 변경 setCount((count)=>count+1);
-
setState
를 통해 같은 값을 주었을 때import {useState} from "react"; const Component = ()=>{ console.log("코드 실행"); const [state,setState] = useState(0); return <div onClick={()=>setState(1)}>{"버튼"}</div>; }
만약 위와 같은 컴포넌트가 있다면 버튼을 여러 번 클릭 했을 때 어떤 식으로 콘솔이 찍힐까. 정답은 몇번을 클릭하든 “코드 실행”이 3번 찍힌다. 우선 처음 렌더링할 때 콘솔이 한번 찍히며, 그 다음 state가 0→1로 변경되어 재렌더링 될 때 한번 찍힌다. 그리고 마지막 state가 1→1이 될 때 한번 찍힌다. 이 때 콘솔은 3번 찍히지만 실제로 렌더링은 2번만 일어난다고 한다. 즉, 같은 값이 들어간 순간 코드 블럭의 코드들이 실행되기는 하지만 이미 그 때부터 렌더링은 진행되지 않는다.