野生のリンクリスト:反応フック


私は先日、反応リストのいくつかのコードを掘り下げていました.


私が飛び込んでいたので、フックが実際にフードの下でリンクリストを使用していることに気づいた.データ構造の実装やユースケースを横切って実行するのは常に冷静であるので、私がどのように、そして、なぜ彼らが使用されているかを共有すると思いました.

実装


を読むフックの実装にコメントがあります.
// Hooks are stored as a linked list on the fiber's memoizedState field. The
// current hook list is the list that belongs to the current fiber. The
// work-in-progress hook list is a new list that will be added to the
// work-in-progress fiber.
繊維が何かわからないなら心配しないで.彼らは基本的に反応の単位のコード表現です.オリジナルからFiber github :
  • pause work and come back to it later.
  • assign priority to different types of work.
  • reuse previously completed work.
  • abort work if it's no longer needed.

したがって、フックはファイバの状態に格納される.ファイバは現在のフックのリンクリストを持つ.何かが反応するとき、ファイバーは進行中のフックリストで新しい仕事を作成して、リストにそれを追加します.
ここからフックの実装ですthe React src (コメント自体はコードそのものです).
function createWorkInProgressHook(): Hook {
  if (workInProgressHook === null) {
    // This is the first hook in the list
    if (firstWorkInProgressHook === null) {
      isReRender = false;
      currentHook = firstCurrentHook;
      if (currentHook === null) {
        // This is a newly mounted hook
        workInProgressHook = createHook();
      } else {
        // Clone the current hook.
        workInProgressHook = cloneHook(currentHook);
      }
      firstWorkInProgressHook = workInProgressHook;
    } else {
      // There's already a work-in-progress. Reuse it.
      isReRender = true;
      currentHook = firstCurrentHook;
      workInProgressHook = firstWorkInProgressHook;
    }
  } else {
    if (workInProgressHook.next === null) {
      isReRender = false;
      let hook;
      if (currentHook === null) {
        // This is a newly mounted hook
        hook = createHook();
      } else {
        currentHook = currentHook.next;
        if (currentHook === null) {
          // This is a newly mounted hook
          hook = createHook();
        } else {
          // Clone the current hook.
          hook = cloneHook(currentHook);
        }
      }
      // Append to the end of the list
      workInProgressHook = workInProgressHook.next = hook;
    } else {
      // There's already a work-in-progress. Reuse it.
      isReRender = true;
      workInProgressHook = workInProgressHook.next;
      currentHook = currentHook !== null ? currentHook.next : null;
    }
  }
  return workInProgressHook;
}
ちょっとこれに飛び込みましょう
if (workInProgressHook === null) {
は、現在使用中のフックがあるかどうかをチェックします.workInProgressHook グローバル変数.
workInProgressHook = createWorkInProgressHook();
それで、workInProgressHook がNULLであれば、どうしますか?
NULL WikinProgresskは現在フックを構築していないことを意味します.つまり、現在のフックで動作したい場合は、リストの次のフックを作成しないことを意味します.私たちがこれをしようとするのは2回あります-私たちが新しいリストを構築して、最初の要素を作り、再レンダリングするとき、既存のリストを再ロードするとき.次if ステートメントはこれを示しています(そして、コメントは私たちに伝えるのに十分なほどです).
// This is the first hook in the list
if (firstWorkInProgressHook === null) {
  isReRender = false;
  currentHook = firstCurrentHook;
  if (currentHook === null) {
    // This is a newly mounted hook
     workInProgressHook = createHook();
  } else {
    // Clone the current hook.
    workInProgressHook = cloneHook(currentHook);
  }
  firstWorkInProgressHook = workInProgressHook;
} else {
  // There's already a work-in-progress. Reuse it.
  isReRender = true;
  currentHook = firstCurrentHook;
  workInProgressHook = firstWorkInProgressHook;
}
我々が持っていないならばfirstWorkInProgressHook , リンクリストを構築する必要があります.しかし、我々が1を持っているならば、それは我々がすでに進行中の仕事をつくっていて、最初の現在のフックがちょうどコピーされることができることを意味します!
次のifステートメントダウン:
if (currentHook === null) {
  // This is a newly mounted hook
   workInProgressHook = createHook();
} else {
  // Clone the current hook.
  workInProgressHook = cloneHook(currentHook);
}
現在、我々は現在のフックを持っているかどうかチェックしたいです.我々がそうしないならば、我々は完全に新しいリストをつくりますcreateHook .

createHook just returns a object with a bunch of null set properties. The one we will be interested in the next property.


さもなければ、これは我々がすでに一度レンダリングしたリストです.反応はちょうどフックをフックして移動することによっていくつかのパフォーマンスを保存し、その方法は、この関数が呼び出されるたびにフックを作成する必要はありません.
だから今私たちの最初のフックを持っている!我々のセットfirstWorkInProgressHook 我々がちょうど作った新しいものへ!
firstWorkInProgressHook = workInProgressHook;
さて、私たちが何を呼ぶかcreateWorkInProgressHook 再び?
function createWorkInProgressHook(): Hook {
  if (workInProgressHook === null) {
    // ...
  } else {
    // what happens here?
  }
  return workInProgressHook;
}
見ましょう!
それでworkInProgressHook NULLではないので、次のフックをチェックする必要があります.
if (workInProgressHook.next === null) {
もし一つもなければ、新しいフックを作成してリストに追加する必要があります.
isReRender = false;
let hook;
if (currentHook === null) {
  // This is a newly mounted hook
  hook = createHook();
} else {
  currentHook = currentHook.next;
  if (currentHook === null) {
    // This is a newly mounted hook
    hook = createHook();
  } else {
    // Clone the current hook.
    hook = cloneHook(currentHook);
  }
}
// Append to the end of the list
workInProgressHook = workInProgressHook.next = hook;
それで、再びクローンに現在のフックがあるかどうかをチェックします(このフックが以前につくられたならば、それが我々に知らせますので、今度は、次のフックが存在するかどうかをチェックします).もしあれば、我々は新しいものを作成しない場合は、それをクローンします.
それから、この魔法の行を呼びます.
workInProgressHook = workInProgressHook.next = hook;
これは、新しく作成されたフックを取る、現在のいずれかの次のフックに設定し、現在の1つの新しいものに等しい設定します.これは基本的に以下のようになります.
workInProgressHook.next = hook;
workInProgressHook = hook;
最後に、我々がすでに進行中のフックを持っているならば、我々はRerenderです、そして、我々が最初にした多くの同じことをしたいです:
} else {
  // There's already a work-in-progress. Reuse it.
  isReRender = true;
  workInProgressHook = workInProgressHook.next;
  currentHook = currentHook !== null ? currentHook.next : null;
}
ここで唯一の違いは、我々は、最後の行にその三元で行われている1つを移動する前に、次のフックであることをcurrentookを更新する必要があります.
いくつかのグローバル変数と関数を使用してリンクリストを作成しました!それはかなりクールです!
この正直なところ、私は実際のコードで使用されるリンクリストを見たのは初めてかもしれません!うまくいけば、このポストは意味をなしました!何か質問があれば教えてください!