flagtags / react-study

0 stars 0 forks source link

리액트 스터디2 #2

Open j03y14 opened 1 year ago

j03y14 commented 1 year ago

궁금한점

  1. state가 실제로 저장되는 위치
  2. 리액트에서 여러개의 hook 식별을 어떻게 하도록 되어있나?
    1. hook이 왜 최상위에 있어야 되나?
j03y14 commented 1 year ago

useState 위치: https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js#L96

j03y14 commented 1 year ago

useState 위치: https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js#L96

useState

  const dispatcher = resolveDispatcher();
  return dispatcher.useReducer(reducer, initialArg, init);

resolveDispatcher

const dispatcher = ReactCurrentDispatcher.current;
  if (__DEV__) {
    if (dispatcher === null) {
      console.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: any): Dispatcher);

invalid hook call 에러가 dispatcher가 없으면 나는 에러였구나. hook 규칙에 안 맞으면 null을 리턴하게 되어있겠네.

ReactCurrentDispatcher


import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';

/**
 * Keeps track of the current dispatcher.
 */
const ReactCurrentDispatcher = {
  current: (null: null | Dispatcher),
};

export default ReactCurrentDispatcher;

current에 언제 넣어주는건데? [[react-fiber]]

ReactFiberBeginWork

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberBeginWork.js#L1092

function updateFunctionComponent() {
    ...
    nextChildren = renderWithHooks(
      current,
      workInProgress,
      Component,
      nextProps,
      context,
      renderLanes,
    );
    ...
}

ReactFiberHooks

https://github.com/facebook/react/blob/612b2b6601abb844248c384d1e288bb824b180b7/packages/react-reconciler/src/ReactFiberHooks.js


ReactCurrentDispatcher.current =
    current === null || current.memoizedState === null
        ? HooksDispatcherOnMount
        : HooksDispatcherOnUpdate;

결국 HooksDispatcherOnMount 이거나 HooksDispatcherOnUpdate 이거 봐야됨


const HooksDispatcherOnMount: Dispatcher = {

    readContext,
    use,
    useCallback: mountCallback,
    useContext: readContext,
    useEffect: mountEffect,

    useImperativeHandle: mountImperativeHandle,

    useLayoutEffect: mountLayoutEffect,

    useInsertionEffect: mountInsertionEffect,

    useMemo: mountMemo,

    useReducer: mountReducer,

    useRef: mountRef,

    useState: mountState,

    useDebugValue: mountDebugValue,

    useDeferredValue: mountDeferredValue,

    useTransition: mountTransition,

    useSyncExternalStore: mountSyncExternalStore,

    useId: mountId,

};

useState는 mountState. 얘는 함수이다.

function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const hook = mountStateImpl(initialState); // hook은 각각의 객체
  const queue = hook.queue;
  const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind(
    null,
    currentlyRenderingFiber,
    queue,
  ): any);
  queue.dispatch = dispatch;
  return [hook.memoizedState, dispatch];
}

state: hook.memoizedState, setState는 dispatch는.

mountStateImpl

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.js#L1727

function mountStateImpl<S>(initialState: (() => S) | S): Hook {
  const hook = mountWorkInProgressHook();
  if (typeof initialState === 'function') {
    // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
    initialState = initialState();
  }
  hook.memoizedState = hook.baseState = initialState;
  const queue: UpdateQueue<S, BasicStateAction<S>> = {
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  };
  hook.queue = queue;
  return hook;
}

mountWorkInProgressHook

function mountWorkInProgressHook(): Hook {
  const hook: Hook = {
    memoizedState: null,

    baseState: null,
    baseQueue: null,
    queue: null,

    next: null, // !!!! 여기서 링크드리스트로 연결되어있음.
  };

  if (workInProgressHook === null) {
    // This is the first hook in the list
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    // Append to the end of the list
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;
}
j03y14 commented 1 year ago

workInProgressHook이 global 하게 let 변수로 저장이 되어있음.

mountWorkInProgressHook에서 next로 훅의 순서를 알 수 있는 것이었음.