James Lin
James Lin

Reputation: 26528

React hooks error: Rendered more hooks than during the previous render

I used to have a function component act as a page:

export default function NormalList(props) {
    const pageSize = 20;
    const [page, setPage] = useState(1)
    const [searchString, setSearchString] = useState(null);
    const [creditNotes, setCreditNotes] = useState(() => getCreditNoteList());
    const [ordering, setOrdering] = useState(null);

    useEffect(() => getCreditNoteList(), [page, searchString, ordering]);

    function getCreditNoteList() {
        API.fetchCreditNoteList({
            account_id: props.customerId, page, page_size: pageSize, searchString, ordering
        }).then(data => {
            setCreditNotes(data);
        });
    }
    return (<>{creditNotes.results.map(record => <...>}</>)
}

And this has been running fine, but recently I need to wrap around NormalList with ListPage component:

export default function ListPage(props) {
    const customerId = props.match.params.customer_id;
    return (<div>...<div><NormalList/></div></div>)
}

Then all the sudden I am getting this error Rendered more hooks than during the previous render.

It seems to me that setCreditNotes(data) inside getCreditNoteList is causing the error, but I don't know why.

Upvotes: 11

Views: 24890

Answers (2)

James Lin
James Lin

Reputation: 26528

OK My problem was that I had 2 issues with importing the elements, first being pycharm cleverly automatically imported them wrong, and secondly being I didn't import one of the component at all.

I wish the error message could be a bit more specific than "Rendered more hooks than during the previous render".

Also I tested I didn't need to move function body getCreditNoteList inside useEffect.

Thanks to @chitova263 for spending the time to help out.

Upvotes: 1

Chitova263
Chitova263

Reputation: 789

So there are a couple of things you need to fix. First of all you should remove your function call from the useState function, you should only perform your side effects inside a useEffect hook see React Docs.

The next thing is whenever you decide to use the dependency array to your useEffect hook you should include all the dependencies of useEffect i.e all the props, state including functions inside your function component that you used inside your useEffect hook. So the Rule of Thumb is Never Lie About Your Dependencies! otherwise you will shoot yourself in the foot.

So the easiest option is to move your getCreditNoteList function inside your useEffect hook and add all the dependencies of your useEffect hook to the dependency array.


export default function NormalList({ customerId }) {
    const pageSize = 20;
    const [page, setPage] = useState(1)
    const [searchString, setSearchString] = useState(null);
    const [creditNotes, setCreditNotes] = useState({});
    const [ordering, setOrdering] = useState(null);

    useEffect(() => {

      function getCreditNoteList() {
        API.fetchCreditNoteList({
            account_id: customerId, 
            page, 
            page_size: pageSize, 
            searchString, 
            ordering
        }).then(data => {
            setCreditNotes(data);
        });
      }

      getCreditNoteList(),

       // add ALL! dependencies
    }, [page, searchString, ordering, pageSize, customerId]))

    return (
       <> </>
    )
}

Second Option If you want to use the getCreditNoteList function elsewhere and want to keep it outside your useEffect hook you can do that by wrapping your getCreditNoteList logic inside the useCallback hook as shown below and add the function to your dependency array inside your useEffect hook for reasons i mentioned earlier.

export default function NormalList({ customerId }) {
    const pageSize = 20;
    const [page, setPage] = useState(1)
    const [searchString, setSearchString] = useState(null);
    const [creditNotes, setCreditNotes] = useState({});
    const [ordering, setOrdering] = useState(null);

    const getCreditNoteList = useCallback(() => {
        API.fetchCreditNoteList({
            account_id: customerId, 
            page, 
            page_size: pageSize, 
            searchString, 
            ordering
        }).then(data => {
            setCreditNotes(data);
        });
     // the function only changes when any of these dependencies change
    },[page, searchString, ordering, pageSize, customerId])

    useEffect(() => {

      getCreditNoteList(),

    },[getCreditNoteList])

    return (
       <> </>
    )
}

Upvotes: 5

Related Questions