Зачем вызывать React Hooks на верхнем уровне? - PullRequest
0 голосов
/ 09 июля 2020

Я читал концепции React-hooks. Я нашел правило, которое гласит Don't call React hooks inside conditions. Здесь они предоставили объяснение ссылка .

function Form() {
  // 1. Use the name state variable
  const [name, setName] = useState('Mary');

  // 2. Use an effect for persisting the form
  useEffect(function persistForm() {
    localStorage.setItem('formData', name);
  });

  // 3. Use the surname state variable
  const [surname, setSurname] = useState('Poppins');

  // 4. Use an effect for updating the title
  useEffect(function updateTitle() {
    document.title = name + ' ' + surname;
  });

  // ...
}

Я понял, что они хотят сказать, но я не могу понять точную причину, например, почему я не могу использовать useEffect в if- else block?

Есть еще один оператор

So how does React know which state corresponds to which useState call?

useState - это разные вызовы каждый раз, и он может возвращать новое "[состояние , setState] "каждый раз, так что здесь сложно узнать, кто какое useState вызвал?

Ответы [ 3 ]

1 голос
/ 09 июля 2020

Из этого ответа документа React он упомянул, что хуки хранятся в "memory cells" и отображаются в порядке (" moves the pointer to the next one")

Существует внутренний список «ячеек памяти», связанных с каждым компонентом. Это всего лишь JavaScript объектов, куда мы можем поместить некоторые данные. Когда вы вызываете Hook, например useState (), он считывает текущую ячейку (или инициализирует ее во время первого рендеринга), а затем перемещает указатель на следующую. Вот как каждый из нескольких вызовов useState () получает независимое локальное состояние.

Это соответствует приведенному ниже разделу ссылки , которую вы предоставили, в которой есть дополнительные объяснения

// Первый рендеринг // ------------

useState ('Mary') // 1. Инициализируйте переменную состояния name с помощью 'Mary'

useEffect (persistForm) // 2. Добавьте эффект для сохранения формы

useState ('Poppins') // 3. Инициализируйте переменную состояния фамилии с помощью 'Poppins'

useEffect (updateTitle) // 4. Добавляем эффект для обновления заголовка

// ------------- // Второй рендер // --------- ----

useState ('Mary') // 1. Прочтите имя переменной состояния (аргумент игнорируется)

useEffect (persistForm) // 2. Замените эффект для сохранения form

useState ('Poppins') // 3. Прочитать переменную состояния фамилии (аргумент игнорируется)

useEffect (updateTitle) // 4. Заменить эффект на обновление заголовка

* 103 6 * Во втором разделе рендеринга в документации сказано, что Read the ... variable означает, что когда useState вызывается во второй раз, он не генерирует новое [состояние, setState], вместо этого приходит "memory cells" для чтения значения состояния. и возвращаемся, затем присваиваем его новому массиву const [state, setState] = useEffect(). Вот почему React может гарантировать, что setState не будет изменяться при каждом повторном рендеринге

React гарантирует, что идентификатор функции setState стабилен и не изменится при повторном рендеринге. Вот почему можно безопасно исключить из списка зависимостей useEffect или useCallback.

1 голос
/ 09 июля 2020

Дело не в том, кто на каком крючке звонил useXXXX (т.е. useState, useEffect, и c). Речь идет о внутренней реализации хуков и их привязке к каждому компоненту. Есть много других проблем, которые для решения React полагаются на порядок вызова хуков.

Из документации Раздел часто задаваемых вопросов по хукам

Как React связывает вызовы Hook с компонентами?

Существует внутренний список «ячеек памяти», связанных с каждым компонентом. Это всего лишь JavaScript объектов, куда мы можем поместить некоторые данные. Когда вы вызываете Hook, например useState (), , он считывает текущую ячейку (или инициализирует ее во время первого рендеринга), а затем перемещает указатель на следующую . Вот как каждый из нескольких вызовов useState () получает независимое локальное состояние.

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

{
  memoizedState: 'a',
  next: {
    memoizedState: 'b',
    next: null
  }
}

Возьмем пример с 4 переменными состояния, вызвав useState 4 раза. При каждом вызове ловушки, если значение не было инициализировано (например, при первом рендеринге ), оно инициализирует значение, которое еще считывается из ячейки памяти, а затем внутренне перемещается к следующей ловушке.

// 4 independent local state variables with their own "memory cell"
// nothing is called conditionally so the call order remains the same
// across renders
useState(1)   // 1st call 
useState(2)   // 2nd call
useState(3)   // 3rd call
useState(4)   // 4th call
useState(1)

if (condition) {   // if condition false hook call will be skipped
  useState(2)   
}

useState(3)   
useState(4)   

Теперь, когда вы вызываете ловушку условно, если условие равно false, вызов ловушки будет пропущен. Это означает, что каждый последующий вызов ловушки будет сдвигаться на 1 в порядке вызова, что приведет к невозможности считывания значения состояния или замене эффекта или многих других ошибок, которые трудно обнаружить. крючок условно. Вызывайте ловушку только на верхнем уровне (не внутри условия, вложенных функций или циклов), что поможет React сохранить состояние ловушек для нескольких вызовов ловушек.

1 голос
/ 09 июля 2020

Басово, ловушка полагается на индекс вызова. React не знает, что вернул данный useState(), поскольку он указывает на предыдущий рендеринг, но он знает, что первый вызов useState() этим компонентом вернул [1,2] в качестве его значения, а второй вызов вернул false. Теперь, если единственное, что знает response, - это каков был данный возврат для данного индекса вызова, как вы думаете, что могло бы произойти, если бы я мог написать такой компонент:

const [a, setA] = React.useState([1,2,3]);
let c;
if(a === [3,2,1]){
  c = React.useState('X');
}
const [b, setB] = React.useState(false);
React.useEffect(() => setA([3,2,1]), []);

сейчас, response знает из сначала визуализируйте, что первый вызов возвращает [1,2,3], а второй - ложь. затем эффект повторно визуализирует компонент, теперь это не первая визуализация, поэтому первый вызов вернет состояние [3,2,1], так как он был обновлен, второй вызов (тот c = ...) вернет false, но затем response видит третий вызов , что он должен вернуть?

С точки зрения реакции, это не имеет смысла, с вашей точки зрения, это может привести к огромному количеству ошибок и проблем.

Конечно, ни мое базовое c объяснение, ни React не так уж и много, поэтому пришедшие источники, Дэн Абрамов, один из людей, работающих в React, имеет очень длинный и подробный пост в своем блоге об этом, вы можете его найти здесь . Он также публикует много других материалов о том, как работает React за кулисами, это стоит прочитать.

...