Если у меня есть крючок, например:

const useGetSet = (label: string) => {
  const [get, set] = useState(label);
  return { get, set };
};

Я могу map использовать массив, например:

const labels = ['one', 'two'].map(useGetSet);

Но если я расширю это до лямбды, например:

const labels = ['one', 'two'].map((l) => useGetSet(l))

Тогда это вызывает:

React Hook "useGetSet" не может быть вызван внутри обратного вызова.
React Hooks должны вызываться в компоненте функции React или в пользовательском React Hook. функция. (Реагировать-крючки / правила-хуки) eslint


Почему такая разница, разве они не равнозначны?

Кроме того, если это нарушение правила перехвата, как следует это сделать?

Полный рабочий пример здесь.

3
davetapley 10 Фев 2021 в 21:30

3 ответа

Лучший ответ

В обоих случаях это плохо, но ESLint не справляется с первым случаем.

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

(Причина, по которой существует правило, заключается в том, что под капотом хуки должны вызываться каждый раз в одном и том же порядке. Очевидно, что если количество хуков варьируется между рендерами, то порядок меняется.)

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

Что касается того, как лучше справиться с этим, если вам нужно больше, чем базовое управление состоянием, что-то вроде { {X0}} подойдет. Вы можете иметь сколько угодно меток и определять действия установщика / получателя, которые принимают метку в качестве аргумента.

2
Will Jenkins 10 Фев 2021 в 19:17

Разберем два сценария:

const labels = ['one', 'two'].map(useGetSet);

В этом случае useGetSet вызывается внутри хука и называется "внутри пользовательской функции React Hook".

const labels = ['one', 'two'].map((l) => useGetSet(l))

В этом случае useGetSet вызывается внутри анонимной функции, поэтому «правило перехватов» нарушается.

Итак, в основном:

В первом сценарии: Hook> вызов ловушки

Во втором сценарии: крючок> анонимная функция> вызов хука

Относительно последнего вопроса:

Почему такая разница, разве они не равнозначны?

Нет, они не эквивалентны.

В первом случае ссылкой на функцию обратного вызова является функция, названная вами useGetSet.

Во втором случае ссылка на функцию обратного вызова - это новая функция, определенная анонимно.

Интересное объяснение того, почему это правило так важно, можно найти в документации, в частности в этом разделе:

https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

Но также учтите, что есть случаи, когда безопасно отключить правило линтера, как подробно описано здесь:

Почему нельзя вызывать React Hooks внутри циклов или вложенных функций?

1
Emanuele Scarabattoli 10 Фев 2021 в 21:10

В этом примере useGetSet - это обратный вызов.

const labels = ['one', 'two'].map(useGetSet);

В этом примере вы вызываете ловушку внутри обратного вызова, чтобы ошибка имела смысл. Этот ответ довольно хорошо объясняет, почему существует правило: Почему нельзя вызывать React Hooks внутри циклов или вложенных функций?

const labels = ['one', 'two'].map((l) => useGetSet(l));

У вас есть 2 варианта:

  1. Если вам нужно вызвать ловушку внутри обратного вызова, но ловушка ничего не отображает, вам нужно создать новую ловушку, например, https://codesandbox.io/s/sad-cache-zti5f?file=/src/TestComponent.jsx.
  2. Если лямбда отображает что-то в конце, вы можете создать новый компонент например, https://codesandbox.io/s/quizzical- currying-pe15r? file = / src / TestComponent.js.
0
Besnik Korça 10 Фев 2021 в 19:01
66142914