Analyse approfondie du code source de React Hooks

Auteur : JD Retail Zheng Bingyi

avant-propos

React Hooksest Reactune nouvelle fonctionnalité introduite dans la version 16.8 qui permet stated'utiliser d'autres fonctionnalités de React dans des composants de fonction sans avoir à utiliser des composants de classe. Hookssont un concept très important car ils offrent une Reactexpérience de développement plus simple et plus compréhensible.

React HooksLe code source principal comprend principalement deux parties : Reactle gestionnaire interne Hooket une série de Hookfonctions prédéfinies .

Tout d'abord, regardons le gestionnaire Reactà l'intérieur . HookCe gestionnaire est Reactun mécanisme interne important chargé de tout gérer dans le composant Hooket de s'assurer qu'ils sont appelés dans le bon ordre lors du rendu du composant.

Gestionnaire de hook interne

Exemple:

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

Dans cet exemple, Hookl'objet a deux propriétés importantes : queueet current. queueStockez toutes Hookles fonctions d'état et de mise à jour dans le composant, et currentstockez une liste liée des composants en cours de rendu Hook. useStateet useHookles fonctions sont responsables de la création d'un nouvel Hookétat et de son utilisation dans le composant , respectivement Hook.

Fonction crochet préréglée

crochet useState

Voici useState Hookun exemple d'implémentation 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;
}

Le code ci-dessus est implémenté useState Hooket sa fonction principale est de renvoyer un statetableau avec la fonction de mise à jour, et la valeur initiale de l'état est initialState.

Dans cette implémentation, updateWorkInProgressHook()la fonction est utilisée pour obtenir l'objet fibre du composant de fonction en cours d'exécution et déterminer s'il en existe un correspondant hook. Il est implémenté comme suit :

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 fonction est utilisée pour obtenir l'objet du composant de fonction en cours d'exécution fiberet workInProgressHookest utilisée pour stocker l' hookobjet en cours d'exécution. Dans un composant de fonction, chaque useStateappel crée un nouvel objet hook et l'ajoute à la liste liée fiberd'objets hooks. Cette hooksliste chaînée est maintenue via les propriétés fiberde l'objet .memoizedState

Nous devons également noter que dans useState Hookl'implémentation de , chaque hookobjet contient un queueobjet pour stocker l'état à mettre à jour et la fonction de mise à jour. scheduleWork()Les fonctions sont utilisées pour notifier Reactau planificateur qu'une tâche doit être exécutée.

Dans Reactle code source de , useStatela fonction est en fait une useStateImplfonction interne appelée .

Voici useStateImplle code source :

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

On peut voir que useStateImplla fonction de la fonction est d'obtenir le courant dispatcheret d'appeler sa useStateméthode, et de retourner un tableau, le premier élément est la valeur de l'état et le deuxième élément est une dispatchfonction pour mettre à jour l'état. La fonction ici resolveDispatcherest utilisée pour obtenir la fonction actuelle dispatcher, et son implémentation est la suivante :

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 fonction essaie d'abord d'obtenir les propriétés de l'objet en cours de rendu dispatcher, et si elle ne peut pas l'obtenir, elle dit

Si le composant n'est pas actuellement dans le processus de rendu, une erreur sera générée.

Enfin, regardons comment la méthode est implémentée useStatedans une implémentation spécifique . dispatchernous useReducerprenons

dispatcherPar exemple, il est implémenté comme suit :

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

Comme vous pouvez le voir, useReducerla méthode appelle en fait une updateReducerfonction appelée , qui renvoie un dispatchtableau contenant l'état et la fonction actuels. updateReducerLa mise en œuvre de est plus compliquée et implique beaucoup de détails, je ne vais donc pas la présenter ici.

useEffect Hook

useEffectEst une fonction Reactcouramment utilisée dans les composants pour effectuer des opérations d'effet secondaire dans les composants, telles que l'accès à des données distantes, l'ajout/la suppression d'écouteurs d'événements, des opérations manuelles , etc. La fonction principale de est d'exécuter la fonction de rappel de manière asynchrone après la fin du processus de rendu du composant, et sa mise en œuvre implique le mécanisme de rendu asynchrone dans React.HookDOMuseEffect

Voici un exemple d'implémentation de useEffect Hook :

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

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

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

Dans cet exemple, useEffectdeux paramètres sont reçus : une fonction de rappel et un tableau de dépendances. Lorsqu'une valeur du tableau des dépendances change,

ReactuseEffectLa fonction callback transmise sera ré-exécutée au prochain rendu .

useEffectL'implémentation de la fonction dépend principalement du Reactmécanisme de rendu asynchrone dans . Lorsqu'un composant doit être rendu à nouveau, Reacttoutes stateles opérations de mise à jour seront ajoutées à une file d'attente, et ces opérations de mise à jour seront exécutées de manière asynchrone après la fin du lot de rendu en cours, évitant ainsi plusieurs opérations de mise à jour consécutives dans le même lot de rendu.

Dans useEffectla fonction, nous useContext(BatchContext)obtenons le lot de rendu actuel en appelant la méthode et shouldFireEffectjugeons si la fonction de rappel doit être exécutée conformément à la méthode. Une fois la fonction de rappel exécutée, nous devons utiliser clearEffectdes méthodes pour effacer les effectinformations d'état actuelles afin d'éviter d'affecter les lots de rendu suivants.

Résumer

En général, React Hooksle principe de mise en œuvre du système n'est pas compliqué, il dépend principalement de Reactla structure de données interne fiberet du système d'ordonnancement, à travers ces mécanismes pour réaliser la gestion et la mise à jour de l'état des composants. HooksCela nous permet d'utiliser l'état et d'autres fonctionnalités dans les composants de fonction React, rendant les composants de fonction comparables aux composants de classe.

En plus de useState, useEffectetc . hook, Reactil en existe useContextd' autres couramment utilisés Hook. Leurs principes d'implémentation sont fondamentalement similaires et ils utilisent tous fiberl'architecture pour implémenter des fonctions telles que la gestion d'état et les crochets de cycle de vie.

Ce qui précède sont hookdes exemples d'implémentation simples, ce ne sont pas Reactles codes réels utilisés dans , mais ils peuvent nous aider à mieux comprendre hookl'implémentation de base.

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

Je suppose que tu aimes

Origine my.oschina.net/u/4090830/blog/8527395
conseillé
Classement