Как React реализует свои хуки (на высоком уровне)

Скажем, у вас есть что-то вроде этого:

const MyComponent = (props) => {
  const refA = React.useRef()
  const refB = React.useRef()
  const refC = React.useRef()
  const [x, setX] = React.useState(true)
  const [y, setY] = React.useState([])

  return <div>...</div>
}

В псевдокоде, что это на самом деле делает, когда вызывается несколько раз? Делают ли они это в принципе?

  • Знайте, что MyComponent вот-вот будет преобразовано из функции в виртуальное дерево.
  • Установите глобальную переменную, которая будет отслеживать каждый вызов хука useX в пределах одного процедурного фрейма.
  • Сохраните для этого экземпляра компонента выходные данные каждого из этих вызовов ловушек.
  • В следующий раз, когда обработчики будут вызываться для этого идентификатора компонента, он получит карту последних возвращенных результатов из последних useX вызовов из этого компонента.

Я спрашиваю, потому что (а) кажется, что это можно сделать только с помощью какого-то трюка с глобальной переменной, и (б) соответствующий код довольно сложен и сложен для анализа.

Интересно, можно ли просто дать краткий обзор высокого уровня или какой-нибудь псевдокод о том, как React реализует эти волшебные хуки.

Кажется, это что-то вроде этого, хотя и более продвинутого:

let dispatcher = {}

function MyComponent() {

}

function begin() {
  dispatcher.component = {
    refs: [],
    states: []
  }
}

function useRef() {
  let ref = {}
  dispatcher.component.refs.push(ref)
  return ref
}

function useState(val) {
  let state = val
  dispatcher.component.states.push(val)
  return state
}

function end() {
  dispatcher.component = null
}

Я просто не понимаю, как он может запоминать и тому подобное с помощью этой магии.

Это не то же самое, что узнать, как хуки узнают, для какого компонента они предназначены. Это только один аспект моего вопроса, я вообще спрашиваю, как работают хуки.


person alien    schedule 09.08.2019    source источник
comment
Я думаю, что внутри он использует редьюсер для отслеживания состояния вашего компонента и возвращает вам метод, который вы можете вызвать, чтобы обновить состояние. Теоретически он глобальный, но все еще ограничен рамками React. Я предлагаю вам просмотреть видео о режиме React Smoosh, в котором освещается множество замечательных деталей того, как работает внутреннее устройство, оно довольно сложное, но очень приятное, когда вы его понимаете: youtube.com/watch?v=aS41Y_eyNrU   -  person Rikin    schedule 09.08.2019
comment
Вы спрашиваете (а) как реализованы хуки или (б) что происходит внутри реакции на каждый вызов?   -  person Dennis Vash    schedule 09.08.2019
comment
Оба, как на самом деле работают хуки, что влечет за собой то, что происходит, когда они вызываются. Мне не нужно знать о части рендеринга, в основном о том, как работают хуки.   -  person alien    schedule 09.08.2019
comment
Описание очень высокого уровня находится в документах: reactjs.org/docs/   -  person Ryan Cogswell    schedule 09.08.2019


Ответы (1)


Вопрос о том, как реализованы хуки, довольно широк, чтобы объяснить каждый хук, возьмем для примера useRef и useState.

Пока useRef(initialValue) — это просто объект с ключом current, возможная реализация:

const myRef = useRef('myReference');
const useRef = (initialValue) => ({ current: initialValue });

Хуки — это просто массивы, этот массив относится к состоянию — и он не используется совместно с другими компонентами, но поддерживается в области, доступной для последующего рендеринга конкретного компонента.

При каждом вызове функции установки мы можем использовать курсор, чтобы определить, какое состояние необходимо использовать.

Возможная реализация useState:

const state = [];
const setters = [];
let firstRun = true;
let cursor = 0;

function createSetter(cursor) {
  return function setterWithCursor(newVal) {
    state[cursor] = newVal;
  };
}

// This is the pseudocode for the useState helper
export function useState(initVal) {
  if (firstRun) {
    state.push(initVal);
    setters.push(createSetter(cursor));
    firstRun = false;
  }

  const setter = setters[cursor];
  const value = state[cursor];

  cursor++;
  return [value, setter];
}

// Our component code that uses hooks
function RenderFunctionComponent() {
  const [firstName, setFirstName] = useState("Rudi"); // cursor: 0
  const [lastName, setLastName] = useState("Yardley"); // cursor: 1

  return (
    <div>
      <Button onClick={() => setFirstName("Richard")}>Richard</Button>
      <Button onClick={() => setFirstName("Fred")}>Fred</Button>
    </div>
  );
}

// This is sort of simulating Reacts rendering cycle
function MyComponent() {
  cursor = 0; // resetting the cursor
  return <RenderFunctionComponent />; // render
}

console.log(state); // Pre-render: []
MyComponent();
console.log(state); // First-render: ['Rudi', 'Yardley']
MyComponent();
console.log(state); // Subsequent-render: ['Rudi', 'Yardley']

// click the 'Fred' button

console.log(state); // After-click: ['Fred', 'Yardley']
person Dennis Vash    schedule 09.08.2019
comment
Ваш useState будет работать только для одного компонента. Как это будет работать для нескольких компонентов? - person alien; 09.08.2019
comment
Ваш useState будет работать только для одного компонента, почему так? - person Dennis Vash; 09.08.2019
comment
if (firstRun) { Как работает мемоизация и т. д. - person alien; 09.08.2019
comment
каждый компонент имеет свое состояние, или я не понимаю, что вы имеете в виду под несколькими компонентами - person Dennis Vash; 09.08.2019
comment
Пожалуйста, прочитайте статью в ответе, нет необходимости сохранять состояние для всех компонентов в приложении, реакция вызывает функцию рендеринга для определенного компонента. - person Dennis Vash; 09.08.2019