야생의 연결 목록: React Hooks

나는 링크드 리스트에 대한 Ali의 훌륭한 게시물을 읽은 직후에 React 후크에 대한 코드 중 일부를 파헤치고 있었습니다.




다이빙을 하던 중 후크가 실제로 후드 아래에서 연결된 목록을 사용하고 있음을 알았습니다. 데이터 구조의 구현 또는 사용 사례에서 실행하는 것은 항상 멋진 일이므로 내가 알 수 있는 한 사용 방법과 이유를 공유해야 한다고 생각했습니다.

구현



후크 구현에는 다음과 같은 주석이 있습니다.

// 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.


섬유가 무엇인지 모르더라도 걱정하지 마십시오. 기본적으로 React에서 작업 단위의 코드 표현입니다. 원본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.


따라서 후크는 파이버의 상태로 저장됩니다. 파이버에는 현재 후크의 연결된 목록이 있습니다. React에서 무언가가 업데이트되면 파이버는 새로운 진행 중인 후크 목록을 생성하고 목록에 추가합니다.

다음은 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 workInProgressHook은 현재 후크를 빌드하고 있지 않음을 의미합니다. 즉, 목록에서 다음 후크를 빌드하지 않고 현재 후크로 작업하려고 합니다. 새 목록을 만들고 첫 번째 요소를 만들 때와 다시 렌더링할 때, 기존 목록을 다시 로드할 때 이렇게 두 번 할 수 있습니다. 다음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 가 없으면 연결 목록 작성을 시작해야 합니다. 여기에는 또 다른 if 문이 있습니다. 그러나 만약 우리가 가지고 있다면 그것은 우리가 이미 진행 중인 작업을 만들고 있고 첫 번째 현재 후크를 복사할 수 있다는 것을 의미합니다!

다음 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.



그렇지 않으면 이미 한 번 렌더링한 목록입니다. React는 이 함수가 호출될 때마다 후크를 다시 생성할 필요가 없도록 훅을 복제하고 계속 진행함으로써 일부 성능을 절약합니다.

이제 첫 번째 후크가 있습니다! 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;


새로 생성된 후크를 가져와 현재 후크의 다음 후크로 설정한 다음 현재 후크를 새 후크와 동일하게 설정합니다. 이것은 기본적으로 다음과 동일합니다.

workInProgressHook.next = hook;
workInProgressHook = hook;


마지막으로 작업 중인 후크가 이미 있는 경우 다시 렌더링하고 처음에 했던 것과 거의 동일한 작업을 수행하려고 합니다.

} else {
  // There's already a work-in-progress. Reuse it.
  isReRender = true;
  workInProgressHook = workInProgressHook.next;
  currentHook = currentHook !== null ? currentHook.next : null;
}


여기서 유일한 차이점은 하나를 이동하기 전에 currentHook을 다음 후크로 업데이트해야 한다는 것입니다. 이 작업은 마지막 줄에서 해당 삼항으로 수행됩니다.

일부 전역 변수와 함수를 사용하여 연결 목록을 만들었습니다! 꽤 괜찮은데!

솔직히 실제 코드에서 사용되는 연결 목록을 본 것은 이번이 처음일 것입니다! 바라건대, 이 게시물이 의미가 있습니다! 질문이 있으면 알려주세요!

좋은 웹페이지 즐겨찾기