135

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…
    // …
  })
  // …
}
7
  • I don't see useRef here ... Commented Aug 7, 2019 at 15:16
  • It's within the Day component, not within Container.
    – Isaac
    Commented Aug 7, 2019 at 15:17
  • 3
    The message tells that hooks are called by Container, not by 'Day'. This seems strange. By any chance, is Event.Event() using hooks?
    – artem
    Commented Aug 7, 2019 at 15:25
  • Yes, it is, useDrag, at the top-level. The ref from it is used, as well as a value returned by a monitor within a closure. Happy to include it, it's the same sort of thing as with Day and Container, though.
    – Isaac
    Commented Aug 8, 2019 at 2:37
  • 4
    If Event.Event is a react component like Day (as I understand from your comment), the problem is that you are calling it directly Event.Event() rather than returning jsx return isTodays && <Event.Event dayHeight={dayHeight} event={e} />
    – OlliM
    Commented Aug 10, 2019 at 14:21

12 Answers 12

115

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 ; }

3
  • 4
    Same here, I did const x = something ? 0 : useMemo(); Commented Nov 19, 2019 at 11:38
  • Thanks! Similarly, conditionally using selectors is not possible. eg: let client = client_id && useSelector(state => state.clients[client_id]) is bad but let client = useSelector(state => client_id ? state.clients[client_id] : null) works fine.
    – pdenya
    Commented Mar 30, 2021 at 17:33
  • Thank you. It seems that React cannot handle short circuiting logic.
    – Elijah
    Commented Apr 3, 2022 at 18:58
60
+100

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.

4
  • It occurred for me, when one of the destructed props object were coming undefined in component, which I fixed by removing unused destructed props object. Commented Oct 30, 2021 at 2:59
  • Nice! For more details calling React components as functions, I found this article helpful (jump to the conclusion for a summary): dev.to/igor_bykov/…
    – bryanbraun
    Commented Jul 29, 2022 at 15:39
  • If you'd like to pass an object's properties as individual props, use spread syntax: <MyComponent {...objectOfProps} /> Source: stackoverflow.com/a/49081573/1465015 and reactjs.org/docs/jsx-in-depth.html#spread-attributes Commented Jan 25, 2023 at 22:42
  • I had <Menu open={isOpen}> → thanks to this response better written as {isOpen && <Menu />} -- It simplified logic inside <Menu > component too! Commented Feb 21, 2023 at 15:56
26

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
1
  • 1
    Thank you for "Getting Error during Test"! This fixed my life. They'll now let me out of the programming cubicle jail.
    – Stephen2
    Commented May 27, 2023 at 16:29
4

calling multiple api calls from useEffect

in my case i was doing multiple api calls and saving each of them to different state, which led me to this error. But after changing it into a single state i was able to overcome that.

const [resp, setGitData] = useState({ data: null, repos: null });

  useEffect(() => {
    const fetchData = async () => {
      const respGlobal = await axios(
        `https://api.github.com/users/${username}`
      );
      const respRepos = await axios(
        `https://api.github.com/users/${username}/repos`
      );

      setGitData({ data: respGlobal.data, repos: respGlobal.data });
    };

    fetchData();
  }, []);
1
  • 3
    What's the problem with this?
    – Gajus
    Commented Jun 6, 2022 at 22:50
3

I know I'm very late, but just wanted to share my experience on the same error stack.

It happened for me for the combination of tanstack's useQuery 4 and useEffect in ReactJS 17.

One beautiful discussion here.

Stacktrace:

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

   Previous render            Next render
   ------------------------------------------------------
1. useContext                 useContext
2. useContext                 useContext
3. useState                   useState
4. useRef                     useRef
5. useEffect                  useEffect
6. useRef                     useRef
7. useEffect                  useEffect
8. useContext                 useContext
9. useState                   useState
10. useRef                    useRef
11. useEffect                 useEffect
12. useRef                    useRef
13. useEffect                 useEffect
14. useState                  useState
15. useState                  useState
16. useState                  useState
17. useState                  useState
18. useState                  useState
19. useState                  useState
20. useContext                useContext
21. useContext                useContext
22. useContext                useContext
23. useContext                useContext
24. useEffect                 useEffect
25. useState                  useState
26. useCallback               useCallback
27. useState                  useState
28. useLayoutEffect           useLayoutEffect
29. useEffect                 useEffect
30. useDebugValue             useDebugValue
31. useEffect                 useEffect
32. undefined                 useEffect
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

So it were happening, due to I were using the useEffect after the useQuery api callback line, which I resolved by putting the useEffect before the useQuery line, and all warning stack trace got solved.

Hope it would help someone.

1
  • Mine was also related to tanstack, but it was bc of a return before a query so for example i was checking the next router for a value and before using that value in a tanstack query, returning null, what fixed it was moving this below the query
    – nmu
    Commented Sep 3, 2023 at 15:01
2

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;
1
  • Another thing to note is check if you are rendering any JS before setting useRef. Such as a loading screen, etc.
    – SJM
    Commented Aug 1, 2023 at 13:23
2

I was getting this error randomly while calling different hook methods in the test I was writing. The fix for me was in the spy of useRef I had implemented:

const useRefSpy = jest
  .spyOn(React, 'useRef')
  .mockReturnValueOnce({ whatever })

Changing the mockReturnValueOnce to mockReturnValue fixed the error.

0

I got the same issue in the react-native application as well. It has happened due to the unnecessary elements loading process.

old code

return (......
     <NotificationDialog
          show={notificationPayload !== null}
          title={notificationPayload?.title}
          .......
        />
);

This will re-render NotificationDialog even notificationPayload !== null. But it is not needed. Simply I added this null check and avoid render this NotificationDialog if it is null

fix

  return (......
         { notificationPayload !== null? <NotificationDialog
              show={notificationPayload !== null}
              title={notificationPayload?.title}
              .......
            /> : null } 
    );
0

i fixed my issue by using an imported component as a component and not as a function, i had to return jsx (<>{values}</>) instead of just values because i had to import redux's useSelector inside ConvertDate, apparently it doesn't work with useSelector if it's just a function. I could've just use a function and pass the date and the redux's state as props though...

what i had:

<b>{ConvertDate({ date: post.dateTime })}</b>

and how i fixed it:

<b>{<ConvertDate date={post.dateTime} />}</b>
0

use eslint-plugin-react-hooks as it will help you easily identify the rootcause in your component code.

React documentation here: https://reactjs.org/docs/hooks-rules.html#eslint-plugin

0

In my case i bring inline useCallback event to outside not its working fine.

   Previous render            Next render
   ------------------------------------------------------
1. useState                   useState
2. useState                   useState
3. useEffect                  useEffect
4. useEffect                  useEffect
5. useCallback               useCallback
6. undefined                 useCallback
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Error:

<EnhancedTable
    onRowClick={useCallback((event, data) => setSelectedRow(data), [])}
    options={isEmpty(selectedRow) ? {
        exportButton: false,
        filtering: false,
        paging: false,
        search: true,
    } : {
        exportButton: false,
        filtering: false,
        paging: false,
        rowStyle: (data, rowData) => data.tableData.id === selectedRow?.tableData?.id && { background: '#1e88e5', color: 'white' },
        search: true,
    }}
    columns={[
        { field: 'applicationUse.application.applicationDisplayName', title: 'Application' },
        { field: 'applicationUse.code', title: 'Application Use' },
        { field: 'shortName', title: 'Short Name' },
        { field: 'entityStatus.code', title: 'Status'},
    ]}
   data={lookUpClients}
   title={''}
/>

Solution:

const onRowClickEvent = useCallback((event, data) => setSelectedRow(data), [])

<EnhancedTable
    onRowClick={onRowClickEvent} // here we given function instead of inline function
    options={isEmpty(selectedRow) ? {
        exportButton: false,
        filtering: false,
        paging: false,
        search: true,
    } : {
        exportButton: false,
        filtering: false,
        paging: false,
        rowStyle: (data, rowData) => data.tableData.id === selectedRow?.tableData?.id && { background: '#1e88e5', color: 'white' },
        search: true,
    }}
    columns={[
        { field: 'applicationUse.application.applicationDisplayName', title: 'Application' },
        { field: 'applicationUse.code', title: 'Application Use' },
        { field: 'shortName', title: 'Short Name' },
        { field: 'entityStatus.code', title: 'Status'},
    ]}
    data={lookUpClients}
    title={''}
/>
0

I was getting following error : Warning: React has detected a change in the order of Hooks called by Body. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks Previous render Next render ------------------------------------------------------

  1. useState useState
  2. useState useState
  3. useState useState
  4. useEffect useEffect
  5. undefined useState

The issue was, while adding custom hook to parent component the sequence got disturbed and hence was throwing error

Not the answer you're looking for? Browse other questions tagged or ask your own question.