How to fix React Error: Rendered fewer hooks than expected

Jon Church
4 min readNov 22, 2018

--

Confused about this error? So was I. Let’s solve it.

Photo by FuYong Hua on Unsplash

If you’re getting this error, there’s a couple of things you can try right away to fix it.

I was able to solve this problem by using component syntax instead of function syntax. I‘m not sure why, but it fixed my problem.

TLDR;

  • Only call hooks from the top level of a component, and never put them in conditional statements.
  • Make sure you only call hooks from within a React Component.
  • Further, pass the component around using Component syntax <MyComponent /> instead of calling the function component directly myComponent() !

First, double check that you’re not calling hooks within a conditional statement. The Hooks documentation makes clear that we are to use hooks at the top of our components, and without nesting them within conditionals.

This is the first, most obvious, issue which can cause the Rendered fewer hooks than expected error.

export default function ListView({history, match}) {
const filter = match.params.filter
let [items, maxPages] = useTopStories(filter, page)
// don't use hooks within a conditional statement!
if (filter === 'jobs') {
const [jobs, setJobs] = useState([])
}


//...
}

Once I was certain I wasn’t making the above mistake, I had to dig around to figure out what else could be rendering fewer hooks than React was looking for.

The Problem

In my code, I was using a Comment function component recursively, mapping over a list of top level comments, and then mapping over each child reply and the reply’s children. The comment component itself looked like this:

export default function Comment({id}) {
const { by, time, text, kids = [] } = useItemSubscription(id)
const [showComments, setShowComments] = useState(true)

return (
<div key={id}>
<div className="text">
{ text && text }
</div>
{
kids.length ? kids.map(id => Comment({id})) : null
}
</div>
)
}

I kept getting the “Rendered fewer hooks than expected” error and was quite frustrated. Eventually, I found this github issue on the React repo discussing it.

The maintainers mentioned “Only call hooks from within React Components, no regular functions.”

Which perplexed me. As I understand it, my Comment function is most certainly a react component. It takes props, it returns JSX. So what gives?

Well, honestly, I’m still not really sure. But I did figure out how to fix it, and maybe someone can help fill in my understanding?

Solution

What I did was: instead of using my functional components like functions, I had to use them like components. I’m not at all clear as to why this is important, but here is my converted code which works:

export default function Comment({id}) {
const { by, time, text, kids = [] } = useItemSubscription(id)
const [showComments, setShowComments] = useState(true)

return (
<div key={id}>
<div className="text">
{ text && text }
</div>
{
kids.length ? kids.map(id => <Comment id={id} />) : null
}
</div>
)
}

Above, all I’m doing is using a different syntax for the component. Instead of using the function syntax, where I am calling my functional component which returns JSX (aka a react component), I’m using the component syntax (I guess thats what it’s called?).

My Theory

To back up a little bit, I’m not totally in the dark as to what was happening. Let me show you a little more of my component tree so you can see for yourself.

Below is the Item component which was using the comment component to begin with:

export default function Item({match}) {
const id = match.params.id
const { title, by, url, score, time, text, kids = [], descendants} = useItemSubscription(id)
return (
<div>
<ItemHeader>
// ... omitted
</ItemHeader>
<ItemComments>
{
kids.length ?
kids.map(id => Comment({id})) : null
}
</ItemComments>
</div>
)
}

As you can see, I’m using the function format here, not the component format. After testing, I found that as long as one of these two components (Item or Comment) is using the component syntax, instead of function syntax, then it works fine.

What doesn’t work though, is using a function call at the top level (Item component), and a function call at the recursive level (Comment component). It’s simple enough to just make them all component syntax, but I was perplexed by this. They’re both functional components, they both take props and return JSX, so what’s going on here?

I’m not too sure, but would love to find out. It seems to me that React doesn’t see that the first level is a component, likely because all these maps are running before the component finished mounting, and maybe at that point React just doesn’t see that the hooks are being called from within a component.

Since Hooks are in such an early stage (I’m on v16.7.0-alpha), things are likely to change soon, so I’m not too concerned about understanding this perfectly right now. For all I know things will work differently in a month.

For now, I’ll be using <Component value={value} /> syntax instead of function syntax Component({id}) when dealing with React hooks!

If this helped you, please give it some claps so others can find it as well!

--

--