0

I'm passing renderProps function in the props. i want to wrap it with useCallback, so the optimised child component will no re-render on the function creation.

when wrapping the function with useCallback i get this error:

Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app

none of the above applies to my situation.

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}
      />
    )
  }, [])
Itay Tur
  • 683
  • 1
  • 15
  • 40
  • by any chance, the component that has `renderCell` method is a class component? If so, you can't use the hooks in it. Note: By the looks of the given snippet, I think your component is a class component. – pritam Aug 24 '20 at 10:12
  • @pritam yes it is, you right. is there a way to achieve the same behaviour with a class component ? – Itay Tur Aug 24 '20 at 10:22
  • I'd rather refactor my parent component to a functional component and keep same implementation of useCallback. Otherwise you could look into `React.memo` and use that for your child component to avoid re-rendering. – pritam Aug 24 '20 at 10:25
  • 1
    @pritam Yea that's what i do. The child has memo, the problem is that because of the render props it always re-render. Currently, i solved it by adding areEqual to the memo. It check the prevProps vs nextProps, and disregard function type props. – Itay Tur Aug 24 '20 at 17:36
  • 1
    Yeah that's right. Memo works for a level deep. If you have multi level data passed down to child or a render prop, you'd need explicit deep checks as part of your memo. – pritam Aug 24 '20 at 17:44

1 Answers1

0

Because hooks doesn't work inside class components, the error was thrown. I managed to find a work around by providing the second parameter for the React.memo. in the function i provided, i compare the prevProps and nextProps, and when the prop is a function i disregard it and return true. it might not work for everyone because sometime the function do change, but for situations when it's not, it's ok.

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)
Itay Tur
  • 683
  • 1
  • 15
  • 40