Análisis en profundidad del código fuente de React Hooks

Autor: JD Retail Zheng Bingyi

prefacio

React Hookses Reactuna nueva característica introducida en 16.8 que permite stateusar otras características de React en componentes de función sin tener que usar componentes de clase. Hooksson un concepto muy importante porque proporcionan una Reactexperiencia de desarrollo más sencilla y comprensible.

React HooksEl código fuente principal incluye principalmente dos partes: Reactel administrador interno y una serie de funciones HookpreestablecidasHook .

Primero, veamos al gerente Reactpor dentro . HookEste administrador es Reactun mecanismo interno importante responsable de administrar todo en el componente Hooky asegurarse de que se llamen en el orden correcto durante la representación del componente.

Administrador de enlace interno

Ejemplo:

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();

En este ejemplo, Hookel objeto tiene dos propiedades importantes: queuey current. queueAlmacene todas Hooklas funciones de estado y actualización en el componente, y currentalmacene una lista vinculada de los componentes que se están procesando actualmente Hook. useStatey useHooklas funciones son responsables de crear un nuevo Hookestado y usarlo en el componente , respectivamente Hook.

Función de gancho preestablecido

Gancho useState

Aquí hay useState Hookun ejemplo de implementación de:

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;
}

El código anterior está implementado useState Hooky su función principal es devolver una statematriz con la función de actualización, y el valor inicial del estado es initialState.

En esta implementación, updateWorkInProgressHook()la función se utiliza para obtener el objeto de fibra del componente de función que se está ejecutando actualmente y determinar si existe uno correspondiente hook. Se implementa de la siguiente manera:

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()La función se usa para obtener el objeto del componente de función que se está ejecutando actualmente fibery workInProgressHookse usa para almacenar el hookobjeto que se está ejecutando actualmente. En un componente de función, cada useStatellamada crea un nuevo objeto de enlace y lo agrega a la lista vinculada fiberde objetos hooks. Esta hookslista enlazada se mantiene a través de las propiedades fiberdel objeto .memoizedState

También debemos tener en cuenta que en useState Hookla implementación de , cada hookobjeto contiene un queueobjeto para almacenar el estado que se actualizará y la función de actualización. scheduleWork()Las funciones se utilizan para notificar Reactal programador que se debe ejecutar una tarea.

En Reactel código fuente de , useStatela función es en realidad una useStateImplfunción interna llamada .

Aquí está useStateImplel código fuente:

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

Se puede ver que useStateImplla función de la función es obtener la corriente dispatchery llamar a su useStatemétodo, y devolver una matriz, el primer elemento es el valor del estado y el segundo elemento es una dispatchfunción para actualizar el estado. La función aquí resolveDispatcherse usa para obtener la actual dispatcher, y su implementación es la siguiente:

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;
}

resolveDispatcherfiberLa función primero intenta obtener las propiedades del objeto que se está representando actualmente dispatcher, y si no puede obtenerlo, dice

Si el componente no se encuentra actualmente en el proceso de renderizado, se generará un error.

Finalmente, echemos un vistazo a cómo se implementa el método useStateen una implementación específica . dispatchertomamos useReducer_

dispatcherPor ejemplo, se implementa de la siguiente manera:

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];
}

Como puede ver, useReducerel método en realidad llama a una updateReducerfunción llamada , que devuelve una dispatchmatriz que contiene el estado y la función actuales. updateReducerLa implementación de es más complicada e involucra muchos detalles, por lo que no la presentaré aquí.

useEffect Hook

useEffectEs una función Reactcomúnmente utilizada en componentes para realizar operaciones de efectos secundarios en componentes, como acceder a datos remotos, agregar/eliminar detectores de eventos, operaciones manuales , etc. La función principal de es ejecutar la función de devolución de llamada de forma asíncrona después de que finaliza el proceso de representación del componente, y su implementación implica el mecanismo de representación asíncrona en React.HookDOMuseEffect

El siguiente es un ejemplo de implementación de useEffect Hook:

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

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

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

En este ejemplo, useEffectse reciben dos parámetros: una función de devolución de llamada y una matriz de dependencias. Cuando cambia cualquier valor en la matriz de dependencias,

ReactuseEffectLa función de devolución de llamada pasada se volverá a ejecutar en la próxima representación .

useEffectLa implementación de la función depende principalmente del Reactmecanismo de representación asincrónica en . Cuando es necesario volver a renderizar un componente, Reacttodas statelas operaciones de actualización se agregarán a una cola y estas operaciones de actualización se ejecutarán de forma asíncrona después del final del lote de renderizado actual, evitando así múltiples operaciones de actualización consecutivas en el mismo lote de renderizado.

En useEffectla función, obtenemos useContext(BatchContext)el lote de representación actual llamando al método y shouldFireEffectjuzgamos si la función de devolución de llamada debe ejecutarse de acuerdo con el método. Después de ejecutar la función de devolución de llamada, debemos usar clearEffectmétodos para borrar la effectinformación del estado actual para evitar que afecte los lotes de renderizado posteriores.

Resumir

En general, React Hooksel principio de implementación del sistema no es complicado, depende principalmente de Reactla fiberestructura de datos internos y el sistema de programación, a través de estos mecanismos para realizar la gestión y actualización del estado del componente. HooksNos permite usar el estado y otras características en componentes de función React, haciendo que los componentes de función sean comparables a los componentes de clase.

Además de useState, useEffectetc . hook, Reactexisten useContextotros de uso común Hook. Sus principios de implementación son básicamente similares y todos usan fiberla arquitectura para implementar funciones como la administración de estado y los enlaces de ciclo de vida.

Los anteriores son hookejemplos de implementación simples, no son Reactlos códigos reales utilizados en , pero pueden ayudarnos a comprender mejor hookla implementación central.

{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/4090830/blog/8527395
Recomendado
Clasificación