Ошибка при вызове обратного вызова в функции renderProps

#javascript #reactjs #react-hooks #usecallback

#javascript #reactjs #реагирующие хуки #обратный вызов

Вопрос:

Я передаю функцию renderProps в реквизит. я хочу обернуть это с помощью useCallback, чтобы оптимизированный дочерний компонент не выполнял повторный рендеринг при создании функции.

при переносе функции с помощью useCallback я получаю эту ошибку:

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

  1. Возможно, у вас несовпадающие версии React и средства визуализации (например, React DOM)
  2. Возможно, вы нарушаете правила использования перехватов
  3. У вас может быть более одной копии React в одном приложении

ни одно из вышеперечисленных не относится к моей ситуации.

 renderCell = React.useCallback((
    {
      events,
      popperPlacement,
      popperStyle,
      time
    }
  ) => {
    const { localeToggle } = this.state;
    const { weekStarter, isTimeShown } = this.props;
    const eventsListPopperStyle = utils.isWeekFirst(time, weekStarter) ||
      utils.isWeekSecond(time, weekStarter) ? { left: '-17% !important' } : { left: '17% !important' };
    return (
      <MonthlyCell
        events={events}
        isTimeShown={isTimeShown}
        popperPlacement={popperPlacement}
        popperStyle={popperStyle}
        time={time}
        eventsListPopperStyle={eventsListPopperStyle}
      />
    )
  }, [])
  

Комментарии:

1. случайно, компонент, у которого есть renderCell метод, не является компонентом класса? Если это так, вы не можете использовать хуки в нем. Примечание: Судя по приведенному фрагменту, я думаю, что ваш компонент является компонентом класса.

2. @pritam да, это так, вы правы. есть ли способ добиться такого же поведения с компонентом класса?

3. Я бы предпочел преобразовать мой родительский компонент в функциональный компонент и сохранить ту же реализацию useCallback. В противном случае вы могли бы изучить React.memo и использовать это для своего дочернего компонента, чтобы избежать повторного рендеринга.

4. @pritam Да, это то, что я делаю. У дочернего элемента есть memo, проблема в том, что из-за реквизитов рендеринга он всегда повторно рендерится. В настоящее время я решил ее, добавив AreEqual в памятку. Он проверяет предварительные запросы по сравнению с nextProps и игнорирует реквизиты типа функции.

5. Да, это верно. Memo работает на более глубоком уровне. Если у вас есть многоуровневые данные, переданные дочернему элементу или реквизиту для рендеринга, вам понадобятся явные глубокие проверки как часть вашей памятки.

Ответ №1:

Поскольку hooks не работает внутри компонентов класса, была выдана ошибка. Мне удалось найти обходной путь, предоставив второй параметр для React.memo. в предоставленной мной функции я сравниваю prevProps и nextProps, и когда prop является функцией, я игнорирую ее и возвращаю true . это может сработать не у всех, потому что иногда функция меняется, но в ситуациях, когда это не так, все в порядке.

 const equalizers = {
  object: (prevProp, nextProp) => JSON.stringify(prevProp) === JSON.stringify(nextProp),


  function: () => true, // disregarding function type props


  string: (prevProp, nextProp) => prevProp === nextProp,
  boolean: (prevProp, nextProp) => prevProp === nextProp,
  number: (prevProp, nextProp) => prevProp === nextProp,
}

export const areEqualProps = (prevProps, nextProps) => {
  for (const prop in prevProps) {
    const prevValue = prevProps[prop];
    const nextValue = nextProps[prop];
    if (!equalizers[typeof prevValue](prevValue, nextValue)) { return false; }
  }
  return true
}

export default React.memo(MyComponent, areEqualProps)