리액트의 주요기능 자체는 컴포넌트와 상태들의 상호작용입니다. 그리고 이러한 주요구성들은 리액트 라이브러리에서 나옵니다. 상태관리에 대해서 짚어봅시다.
상태가 업데이트 되면 컨포넌트 자체가 재실행되면서 모든 상태들이 초기화 될 것 같습니다. 하지만 막상 useState를 써보면 이전에 쓰던 상태들이 그대로 남아있는 것을 확인 할 수 있습니다.
대표로 useState가 어떤 식으로 실행되는지 살펴봅시다.
리액트는 컴포넌트를 재실행할 때 state가 어떤 DOM에서 사용되는지 알고 있습니다. ( 클로져 ) 가장 처음에는 useState(init) 에 해당하는 초기값으로 state값을 배정합니다. 이후에 컴포넌트가 재평가 되어 useState 코드가 실행될 때 useState는 다시 값을 초기화 하지 않습니다. 리액트는 이구성 요소에 대한 상태가 이미 있음을 인지하고 ( 클로저 ) 필요에 따라 해당 상태를 업데이트 합니다. 해당 컴포넌트가 DOM구성요소에서 완전히 제거가 되지 않는한 재평가시 초기화하지 않습니다. DOM구성요소에서 완전히 제거되었다가 다시 렌더링되면 해당 제거된 컴포넌트의 상태변수는 다시 초기화되게 됩니다.
리액트의 다른 상태 변경 함수들도 마찬가지 입니다.
리액트 상태관리 훅을 이용해서 상태관리를 할경우 시나리오를 봅시다.
우리 코드에서 가장처음에는 useState로 인한 초기값이 상태변수에 들어갑니다. 이후 우리 코드에 의해 setState 코드가 실행되면 즉시 변경된 값이 반영되지 않습니다.
리액트는 단지 해당 변경이 일어난다는 것을 예약 해놓습니다. 왜냐면 setState 보다 다른 우선순위가 있는 일들이 있고 setState가 여러변 변경되었을 경우 한번에 그 변경을 반영해야하기 때문입니다. 그렇게 변경을 예약해놓은다음 상태가 변경이 되면. 컴포넌트가 재실행되어 우리 코드에 해당 값이 반영됩니다.
setState(prev => prev!);
이렇게 이전값을 참조하는 형태로 상태변경코드를 짤 경우 상태변경은 순서대로 이뤄집니다. 계속 해서 이전값을 참조하기때문에 마지막에 업데이트 되는 값은 가장 최신의 값을 참조하게 되기 때문입니다. 이전 값을 참조해야하는데 이전 값을 참조하지않는 형태로 코드를 짜지 않을 경우는 setState는 최신의 값을 받지 못할 수 있습니다.
const [state, setState] = useState(0);
setState( state + 1 );
setState( state + 1 );
// state === 2 : false
// state === 1 : true
const [state, setState] = useState(0);
setState(prev => prev + 1);
setState(prev => prev + 1);
// state === 2 : true
이렇게 setState로 상태변경이 예정되었지만 아직 반영되지 않은 경우에 최신상태를 항상반영하면서 상태변경을 하려면 인라인 함수를 이용해서 이전 상태값을 반영시켜줘야합니다.
또한 리액트는 setState 변경시 바로 여러줄의 setState 호출이 이뤄진다면 해당 프로세스들을 한묶음으로 한번에 스케쥴링을 합니다. 따로따로 업데이트 하는 것이 아닌 여러개의 상태들을 한번에 업데이트 합니다.