Jordan
Jordan

Reputation: 4803

How to conditionally render a React component that uses hooks

The following (albeit contrived) component violates react-hooks/rules-of-hooks (via eslint)

function Apple(props){
  const {seeds} = props;
  if(!seeds){
    return null;
  }
  const [bitesTaken, setBitesTaken] = useState(0);
  return <div>{bitesTaken}</div>
}

with the following error React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?

I understand that hooks need to be called in the same order, but this kind of conditional rendering (so long as no hooks are invoked before the return, does not prevent the hooks from being called in the same order. I think the eslint rule is too strict, but maybe there's a better way.

What is the best way to conditionally render a component which uses hooks?

Upvotes: 4

Views: 9075

Answers (2)

BertC
BertC

Reputation: 2656

Another option is to split the component.

import React,{useState} from 'react';

const Apple = (props) => {
    const {seeds} = props;
    return !!seeds && <AppleWithSeeds />;
};

const AppleWithSeeds =(props) => {
    const [bitesTaken] = useState(0);
    return <div>{bitesTaken}</div>
};

export default Apple;

Advantage of this method is that your components stay small and logical.

And in your case you might have something more than '0' in the useState initializer which you don't want unnecessary at the top before the condition.

Upvotes: 5

JMadelaine
JMadelaine

Reputation: 2964

You can't conditionally use hooks, so move the hook above the condition.

function Apple(props){
  const {seeds} = props;
  const [bitesTaken, setBitesTaken] = useState(0);
  if(!seeds){
    return null;
  }
  return <div>{bitesTaken}</div>
}

You can also simplify the rendering like this:

function Apple(props) {
  const { seeds } = props
  const [bitesTaken, setBitesTaken] = useState(0)
  return !!seeds && <div>{bitesTaken}</div>
}

If seeds is falsey, then false will be returned (which renders as nothing), otherwise, the div will be returned.

I've added the double exclamation point to seeds to turn it into a boolean, because if seeds is undefined then nothing is returned from render and React throws an error.

Upvotes: 4

Related Questions