In-depth analysis of React Hooks source code

Author: JD Retail Zheng Bingyi

foreword

React Hooksis Reacta new feature introduced in 16.8 that allows stateand other React features to be used in function components without having to use class components. Hooksare a very important concept because they provide a simpler and more understandable Reactdevelopment experience.

React HooksThe core source code mainly includes two parts: Reactthe internal Hookmanager and a series of preset Hookfunctions .

First, let's look at the manager Reactinside . HookThis manager is Reactan important internal mechanism responsible for managing everything in the component Hookand making sure they are called in the correct order during component rendering.

Internal Hook Manager

Example:

const Hook = {
  queue: [],
  current: null,
};

function useState(initialState) {
  const state = Hook.current[Hook.queue.length];
  if (!state) {
    Hook.queue.push({
      state: typeof initialState === 'function' ? initialState() : initialState,
      setState(value) {
        this.state = value;
        render();
      },
    });
  }
  return [state.state, state.setState.bind(state)];
}

function useHook(callback) {
  Hook.current = {
    __proto__: Hook.current,
  };
  try {
    callback();
  } finally {
    Hook.current = Hook.current.__proto__;
  }
}

function render() {
  useHook(() => {
    const [count, setCount] = useState(0);
    console.log('count:', count);
    setTimeout(() => {
      setCount(count + 1);
    }, 1000);
  });
}

render();

In this example, Hookthe object has two important properties: queueand current. queueStore all Hookstate and update functions in the component, and currentstore a linked list of components currently being rendered Hook. useStateand useHookfunctions are responsible for creating new Hookstate and using it in the component , respectively Hook.

Preset Hook function

useState Hook

Here is useState Hookan example implementation of :

function useState(initialState) {
  const hook = updateWorkInProgressHook();
  if (!hook.memoizedState) {
    hook.memoizedState = [
      typeof initialState === 'function' ? initialState() : initialState,
      action => {
        hook.queue.pending = true;
        hook.queue.dispatch = action;
        scheduleWork();
      },
    ];
  }
  return hook.memoizedState;
}

The above code is implemented useState Hook, and its main function is to return an statearray with the update function, and the initial value of the state is initialState.

In this implementation, updateWorkInProgressHook()the function is used to obtain the fiber object of the currently executing function component and determine whether there is a corresponding one hook. It is implemented as follows:

function updateWorkInProgressHook() {
  const fiber = getWorkInProgressFiber();
  let hook = fiber.memoizedState;
  if (hook) {
    fiber.memoizedState = hook.next;
    hook.next = null;
  } else {
    hook = {
      memoizedState: null,
      queue: {
        pending: null,
        dispatch: null,
        last: null,
      },
      next: null,
    };
  }
  workInProgressHook = hook;
  return hook;
}

getWorkInProgressFiber()The function is used to obtain the object of the currently executing function component fiber, workInProgressHookand is used to store the currently executing hookobject. In a function component, each useStatecall creates a new hook object and adds it to the linked list fiberof objects hooks. This hookslinked list is maintained through the properties fiberof the object .memoizedState

We also need to note that in useState Hookthe implementation of , each hookobject contains an queueobject to store the state to be updated and the update function. scheduleWork()Functions are used to notify Reactthe scheduler that a task needs to be executed.

In Reactthe source code of , useStatethe function is actually an useStateImplinternal function called .

Here is useStateImplthe source code:

function useStateImpl<S>(initialState: (() => S) | S): [S, Dispatch<SetStateAction<S>>] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

It can be seen that useStateImplthe function of the function is to obtain the current dispatcherand call its useStatemethod, and return an array, the first element is the value of the state, and the second element is a dispatchfunction to update the state. The function here resolveDispatcheris used to get the current one dispatcher, and its implementation is as follows:

function resolveDispatcher(): Dispatcher {
  const dispatcher = currentlyRenderingFiber?.dispatcher;
  if (dispatcher === undefined) {
    throw new Error('Hooks can only be called inside the body of a function component. (https://fb.me/react-invalid-hook-call)');
  }
  return dispatcher;
}

resolveDispatcherfiberThe function first tries to get the properties of the object currently being rendered dispatcher, and if it cannot get it, it says

If the component is not currently in the rendering process, an error will be thrown.

Finally, let's take a look at how the method is implemented useStatein a specific implementation. dispatcherwe useReducertake

dispatcherFor example, it is implemented as follows:

export function useReducer<S, A>(
  reducer: (prevState: S, action: A) => S,
  initialState: S,
  initialAction?: A,
): [S, Dispatch<A>] {
  const [dispatch, currentState] = updateReducer<S, A>(
    reducer,
    // $FlowFixMe: Flow doesn't like mixed types
    [initialState, initialAction],
    // $FlowFixMe: Flow doesn't like mixed types
    reducer === basicStateReducer ? basicStateReducer : updateStateReducer,
  );
  return [currentState, dispatch];
}

As you can see, useReducerthe method actually calls a updateReducerfunction called , which returns an dispatcharray containing the current state and function. updateReducerThe implementation of is more complicated and involves a lot of details, so I won't introduce it here.

useEffect Hook

useEffectIs a function Reactcommonly used in components to perform side-effect operations in components, such as accessing remote data, adding/removing event listeners, manual operations , and so on. The core function of is to execute the callback function asynchronously after the rendering process of the component ends, and its implementation involves the asynchronous rendering mechanism in React.HookDOMuseEffect

The following is an example implementation of useEffect Hook:

function useEffect(callback, dependencies) {
  // 通过调用 useLayoutEffect 或者 useEffect 方法来获取当前的渲染批次
  const batch = useContext(BatchContext);

  // 根据当前的渲染批次判断是否需要执行回调函数
  if (shouldFireEffect(batch, dependencies)) {
    callback();
  }

  // 在组件被卸载时清除当前 effect 的状态信息
  return () => clearEffect(batch);
}

In this example, useEffecttwo parameters are received: a callback function and an array of dependencies. When any value in the dependencies array changes,

ReactuseEffectThe callback function passed in will be re-executed at the next rendering .

useEffectThe implementation of the function mainly depends on Reactthe asynchronous rendering mechanism in . When a component needs to be re-rendered, Reactall stateupdate operations will be added to a queue, and these update operations will be executed asynchronously after the end of the current rendering batch, thereby avoiding multiple consecutive update operations in the same rendering batch.

In useEffectthe function, we useContext(BatchContext)get the current rendering batch by calling the method, and shouldFireEffectjudge whether the callback function needs to be executed according to the method. After the callback function is executed, we need to use clearEffectmethods to clear the current effectstate information to avoid affecting subsequent rendering batches.

Summarize

In general, React Hooksthe implementation principle of the system is not complicated, it mainly depends on Reactthe internal fiberdata structure and scheduling system, through these mechanisms to realize the management and update of the component state. HooksIt allows us to use state and other features in function components React, making function components comparable to class components.

In addition to useState, useEffectetc. hook, Reactthere are useContextother commonly used ones Hook. Their implementation principles are basically similar, and they all use fiberthe architecture to implement functions such as state management and lifecycle hooks.

The above are hooksimple implementation examples, they are not Reactthe actual codes used in , but they can help us better understand hookthe core implementation.

{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/8527395