React useState에 대해 궁금해졌다 1
궁금해서 node_modules의 react 폴더를 직접 들어가봤다.
모듈을 볼땐 index.js를 먼저 보는 편이다.
// index.js
'use strict';
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react.production.min.js');
} else {
module.exports = require('./cjs/react.development.js');
}
dev모드랑 production모드가 무엇인가 차이가 있나보다.
일단 react.development.js부터 보자.
useState가 언급돼있을까?
있다!
function useState(initialState) {
var dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
// 생략
exports.useState = useState;
useState를 파일 내에서 검색해봤더니 해당 부분에서 언급돼있다.
수천줄의 코드가 있는 코드라 흐름은 알기 어렵다.
우선 resolveDispathcer()이 무엇인지 봐야겠다.
// resolveDispatcher()가 사용된 곳
function useState(initialState) {
var dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
function useReducer(reducer, initialArg, init) {
var dispatcher = resolveDispatcher();
return dispatcher.useReducer(reducer, initialArg, init);
}
function useRef(initialValue) {
var dispatcher = resolveDispatcher();
return dispatcher.useRef(initialValue);
}
function useEffect(create, deps) {
var dispatcher = resolveDispatcher();
return dispatcher.useEffect(create, deps);
}
function useInsertionEffect(create, deps) {
var dispatcher = resolveDispatcher();
return dispatcher.useInsertionEffect(create, deps);
}
function useLayoutEffect(create, deps) {
var dispatcher = resolveDispatcher();
return dispatcher.useLayoutEffect(create, deps);
}
function useCallback(callback, deps) {
var dispatcher = resolveDispatcher();
return dispatcher.useCallback(callback, deps);
}
function useMemo(create, deps) {
var dispatcher = resolveDispatcher();
return dispatcher.useMemo(create, deps);
}
function useImperativeHandle(ref, create, deps) {
var dispatcher = resolveDispatcher();
return dispatcher.useImperativeHandle(ref, create, deps);
}
function useDebugValue(value, formatterFn) {
{
var dispatcher = resolveDispatcher();
return dispatcher.useDebugValue(value, formatterFn);
}
}
function useTransition() {
var dispatcher = resolveDispatcher();
return dispatcher.useTransition();
}
function useDeferredValue(value) {
var dispatcher = resolveDispatcher();
return dispatcher.useDeferredValue(value);
}
useState뿐만 아니라 우리가 쓰는 hook에 들어가있는 무엇인가이다.
function resolveDispatcher() {
var dispatcher = ReactCurrentDispatcher.current;
{
if (dispatcher === null) {
error('Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + ' one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + '2. You might be breaking the Rules of Hooks\n' + '3. You might have more than one copy of React in the same app\n' + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.');
}
} // Will result in a null access error if accessed outside render phase. We
// intentionally don't throw our own error because this is in a hot path.
// Also helps ensure this is inlined.
return dispatcher;
}
익숙한 에러메시지가 보인다. 주석을 읽어보자.
1. 렌더 페이즈 외부에서 접근할 때 null access error 발생
2. (성능이) 매우 중요한 경로에 있으므로 자체 오류를 발생시키지 않는다.
3. 이것이 인라인임을 보장하는데 도움된다.
1번과 2번은 연관시킬 수 있을 것 같다.
오류가 발생하고 오류를 처리하고 오류를 던지는 것은 성능 저하를 발생시킨다. 따라서 자체오류를 발생시키지 않는다. 그대신, 예상가능한 null access error는 발생시키게 한다.
3번은 잘 모르겠다.
inline은 <div style={{width: '100%',height:'500px'}}/>와 같은 인라인 스타일을 사용할때, 또는 <script ></script>안에 js를 작성하는 자바스크립트 작성 방식에서 언급된 단어이다.
React가 랜더링 되는 동안 작성되는 인라인 함수도 있겠다.
아! useState가 컴포넌트 외부가 아니라 내부에서 작성된다는걸 저렇게 표현한 것인 것 같다
이제 ReactCurrentDispatcher가 궁금해졌다.
// ReactCurrentDispatcher가 사용된 곳(후략)
/**
* Keeps track of the current dispatcher.
*/
var ReactCurrentDispatcher = {
/**
* @internal
* @type {ReactComponent}
*/
current: null
};
var ReactSharedInternals = {
ReactCurrentDispatcher: ReactCurrentDispatcher,
ReactCurrentBatchConfig: ReactCurrentBatchConfig,
ReactCurrentOwner: ReactCurrentOwner
};
//위에서 본 것
function resolveDispatcher() {
var dispatcher = ReactCurrentDispatcher.current;
{
if (dispatcher === null) {
error('Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + ' one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + '2. You might be breaking the Rules of Hooks\n' + '3. You might have more than one copy of React in the same app\n' + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.');
}
} // Will result in a null access error if accessed outside render phase. We
// intentionally don't throw our own error because this is in a hot path.
// Also helps ensure this is inlined.
return dispatcher;
}
// 그리고 $1이라는 추가적인 변수도 보았다. 생략한다.
var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher;
일단 이것 부터 보자.
/**
* Keeps track of the current dispatcher.
*/
var ReactCurrentDispatcher = {
/**
* @internal
* @type {ReactComponent}
*/
current: null
};
internal: 내부에서만 쓰이는 코드
type: current는 react component이다.
즉 current는 현재 dispatcher가 다루고 있는(?) 컴포넌트를 나타낸다.
근데.. 여기서 끝난다.
keeps track of the current dispatcher이라는 주석 뿐이다.
하지만 index.js말고도 다른 파일들이 있다.
continue;