"React has detected a change in the order of Hooks" but Hooks seem to be invoked in order

I am trying to use Context and Reducers via React's hooks, and running into problems with the order of the hooks not being constant. My understanding was that as long as the order of the useHook(…) remained the same, it was fine to invoke the returned state/update function/reducer in any sort of control flow. Otherwise, I'm invoking the hooks at the very beginning of the FunctionComponents.

Is it that I'm generating Days in a loop? Or missing something else?

Warning: React has detected a change in the order of Hooks
called by Container. This will lead to bugs and errors if not fixed. For
more information, read the Rules of Hooks:
https://reactjs.org/docs/hooks-rules.html

   Previous render            Next render
   ------------------------------------------------------
1. useContext                 useContext
2. undefined                  useRef
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The full version of Container is below. An excerpt from Day is below, and has a ref from react-dnd's useDrop.

export const Container: FunctionComponent<Props> = () => {
  let events = useContext(State.StateContext)
  //let events: Array<Event.Event> = [] <- with this, no warning

  const getDaysEvents = (day: Event.Time, events: Array<Event.Event>) => {
    return events.map(e => {
      const isTodays = e.startTime.hasSame(day, "day")
      return isTodays && Event.Event({ dayHeight, event: e })
    })
  }

  let days = []
  for (let i = 0; i < 7; i++) {
    const day = DateTime.today().plus({ days: i })
    days.push(
      <Day key={day.toISO()} height={dayHeight} date={day}>
        {getDaysEvents(day, events)}
      </Day>
    )
  }
  return <div className="Container">{days}</div>
}

An excerpt from Day (Event similarly uses a useDrag hook, also called at the top level just like here).

const Day: FunctionComponent<DayProps> = ({ date, height, children }) => {
  const dispatch = useContext(State.DispatchContext)
  const [{ isOver, offset }, dropRef] = useDrop({
    // …uses the dispatch function within…
    // …
  })
  // …
}

I ran into this same error message in a component I was writing due to use of short-circuiting logic.

This resulted in an error:

const x = useSelector(state => state.foo);
if (!x) { return ; }
const y = useSelector(state => state.bar);

This is because when x is truthy the list of hooks has length 2, but when x is falsey the list has length 1.

To resolve the error I had to put all hook use before any early terminations.

const x = useSelector(state => state.foo);
const y = useSelector(state => state.bar);
if (!x) { return ; }


Writing my comment as an answer:

The problem is that you're calling Event.Event() directly, even though it is a react component. That causes react to treat the hook calls inside the function as part of Container, even though you meant for them to be part of Event.

The solution is to use JSX:

return isTodays && <Event.Event dayHeight={dayHeight} event={e} />

Why this works is clearer when you replace the JSX with the resulting JS code:

return isTodays && React.createElement(Event.Event, { dayHeight, event: e })

See https://reactjs.org/docs/react-api.html#createelement. You never want to call the function components directly, how react works is that you always hand a reference the component to react and let it call the function at the correct time.


For other reason instead of this question, when you receive this error

enter image description here

It actually happens because of any bad practice for hook implementations

1 - Only Call Hooks at the Top Level

Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function

NOTE: Implement your useState hooks first at top of the function

2 - Only Call Hooks from React Functions

Don’t call Hooks from regular JavaScript functions

3 - Getting Error during Test

If you get This Error when testing the component, be careful to where you set your custom hooks (replace to top of the function)

Best Practice

use eslint for lint your code avoid geting React Hooks Rules errors

install package with npm or yarn

npm install eslint-plugin-react-hooks --save-dev

It is not the question scenario, but it is the error itself, hopefully, it will help somebody :)

  const { chatSession, userBelongsToSession } = useChatSession(session_id)
  const { activeSession, setActiveSession } = useActiveChatSession()

  const isCurrentActiveSession = useMemo(() => activeSession != null && activeSession.session_id === session_id, [activeSession, session_id])

  if (chatSession == null || activeSession == null) {
    return (<div></div>)
  }

  const Container = styled(BorderedContainer)`height: 72px; cursor: pointer;`

The same error happens to me in this piece of code and is related to useRef being called by styled-components and not being called before because of conditional rendering

if (chatSession == null || activeSession == null) {
  return (<div></div>)
}

by default, the hooks will return null and my component will render with no useRef, although when the hooks are actually populated, styled-components will then generate a component with useRef.

  if (chatSession == null || activeSession == null) {
    return (<div></div>)
  }

const Container = styled(BorderedContainer)`height: 72px; cursor: pointer;