react/index.js
에서 react/src/React
에서 export한 useState를 export하고 있다
react/src/React.js
에서는 react/src/ReactHooks.js
에서 export한 useState를 export하고 있다
react/src/ReactHooks.js
에서는 resolveDispatcher
가 반환하는 dispatcher
의 useState프로퍼티에 initialState를 넣어서 반환하고 있다.
export function useState<S>(
initialState: (() => S | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
resolveDispatcher
함수는 ReactCurrentDispatcher.current
를 반환 하는데 이 때 ReactCurrentDispatcher.current
가 null일 경우 error를 출력한다.
- render phase 밖에서 접근한다면 error를 발생 시키는 것이 아니라 null을 반환하여 엑세스 오류가 발생하도록 한다.
- render phase가 아닌 다른 상황에서 호출될 경우 dispatcher가 null일 수 있다
- error를 throw하지 않고 null을 반환하여 엑세스 오류를 발생하게 하여 최적화오 ㅏ불필요한 오버헤드를 방지할 수 있다
ReactCurrentDispatcher
는 react/src/ReactCurrentDispatcher.js
에서 import 한다.
ReactCurrentDispatcher
는 current
라는 key 값을 가진 객체이다.
const ReactCurrentDispatcher = {
current : (null : null | Dispatcher),
};
export default ReactCurrentDispatcher;
State가 저장되는 구조
- 함수형 컴포넌트는 그 안에 값을 저장하지 않기 때문에, state 값을 밖에서 불러와야 한다.
- state의 값들은 배열에 실행 순서대로 저장된다.
- 이 배열은 리액트 런타임이 관리하는 FiberNode라는 곳에 저장 되어 있다.
- 컴포넌트는 처음 렌더링(=함수가 실행)될 때. useState를 마주칠 때마다 빈 배열에 state값을 하나 씩 push한다.
- 그리고 이 배열은 컴포넌트가 마운트 되어 있는 한 유지된다.
- 함수가 실행되는 동안 훅의 순서는 불변한다.
- 리액트는 함수형 컴포넌트를 실행할 때 배열에서 순서대로 값을 읽어오면 각각의 state는 배열의 같은 index에서 값을 읽어오게 된다.
- 만약 조건부로 useState가 실행되면, 특정 렌더에서 조건문에 따라 실행되지 않는 훅이 생기면, 이후 실행되는 모든 state값들의 index가 달라진다.
- 그러면 리액트는 누가 누구인지 구분할 수 없게 되므로, state는 무조건 실행 되어야 한다.
- 훅이 조건문 내부나, return 아래에 위치하면 안된다는 제약은 useEffect 훅에도 똑같이 적용된다
- 이는 state가 배열에 저장되는 것과 같은 방식으로 각 effect의 dependency array를 저장하기 때문이다.
- 리액트는 매 렌더링마다 dependency array의 값을 이전과 비교하여 effect 함수를 실행할지 여부를 판단한다.
- state가 업데이트 될 때에는, 해당 배열에 저장된 값을 바꿔주면 된다
- 이 때 state에 새로운 값을 배정하는 것이 아니라 setState를 통해서 새로운 값을 배정한다.
setState()는 setter가 아닌, Dispatch 요청이다