catching error in GET request is retrieving Uncaught Error: Rendered fewer hooks than expected

I am having some difficulties with checking the side scenario of an API request.
I have this component:

import * as React from "react";
import {
    getOrganizationByObjectId,
    updateOrganization
} from "../../../services/apiRequests";
import {useEffect, useState} from "react";
import {useParams} from "react-router-dom";
import {formStructure, yupValidation, defaultValue} from "../../../schemas/OrganizationSchemas.jsx";
import {UPDATE} from "../../../utils/definitions.jsx";
import FormEntity from "../../../components/FormEntity.jsx";
import Message from "../../../components/Message.jsx";
import {BeatLoader} from "react-spinners";

const EditOrganization = () => {
    const [values, setValues] = useState(defaultValue());
    //const [loading, setLoading] = useState(true);
    const [errorMsg, setErrorMsg] = useState("");
    const {objectId} = useParams();

    useEffect(() => {
        getOrganizationByObjectId(objectId)
            .then((response) => setValues(response))
            .catch((error) => {
                setErrorMsg(error.error);
            });
    }, [objectId]);

    const renderFormEntity = () => {
        if (errorMsg === "") {
            return (
                <FormEntity
                    formStructure={formStructure()}
                    yupValidation={yupValidation()}
                    submitMethod={updateOrganization}
                    title={"Edit Organization"}
                    values={values}
                    formType={UPDATE}
                    forwardToAfterRequest={'/organizations/list'}
                />
            );
        }
        return null;
    };

    return (
        <>
            <Message message={errorMsg} severity={"error"}/>
            {renderFormEntity()}
        </>
    );
};

export default EditOrganization;

On the first run, it should fetch the data from the server using the useEffect() hook.
while giving proper and valid ID, and no error has been caught by the hook everything works perfectly.

But when I deliver some wrong ID the server receives an exception, and the expectation from the code is to store it inside the state errorMsg.

  • I have checked and error. the error.error has the correct value.

for some reason, I am getting this error:

Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.

I have debugged the code, and the error is related to the state errorMsg when removing that setErrorMsg() the code is working perfectly but without the error message..

if the errorMsg is not empty I am generating the Message component:

const Message = ({message, severity}) => {
    return (
        message !== '' &&
        <Alert severity={severity} sx={{mt: 3, mb: 3, width: "100%"}}>
            <div style={{ marginRight: '20px' }}>{message}</div>
        </Alert>
    );
}

What am I missing here?

  • what do you believe ({message, severity}) => { returns when message is empty string?

    – 

  • @JaromandaX, it should return anything I have the condition message !== "" for that validation

    – 

  • so, if message is NOT empty, it returns <Alert ... … but if it IS empty, it returns what? hint, it returns boolean value false – I don’t know enough about react to know if that’s an issue. In your other code, you return null when the condition isn’t met

    – 




  • I have changed the component to be more accurate, if message and severity is ” it will return null and the results are the same.

    – 




  • OK, as I said, don’t know react well enough to know if that’s an issue

    – 

When using Functional Components in React, you need to take care that your code should contain same number of hooks regardless of your props or state.

So, doing something like

import React, { useState } from "react";

export default function App() {
  const [name, setName] = useState("A");

  if (name === "B") {
    const [age, setAge] = useState(10);
  }

  return <div onClick={() => setName("B")}>Hello</div>;
}

will give an error any time the div is clicked. This is bacause, after clicking on the div, the name state was changes to B. This resulted in the if statement being executed where we called another hook for [age, setAge].

In your code, I don’t see this kind of code being written, so my best guess is that in your renderFormEntity method

const renderFormEntity = () => {
  if (errorMsg === "") {
    return (
      <FormEntity
        formStructure={formStructure()}
        yupValidation={yupValidation()}
        submitMethod={updateOrganization}
        title={"Edit Organization"}
        values={values}
        formType={UPDATE}
        forwardToAfterRequest={"/organizations/list"}
      />
    );
  }
  return null;
};

where the if statement becomes true, the formStructure and yupValidation methods are called. I am guessing the formStructure method is actually a functional component containing some hooks.

If that is the case, there are 2 possible solutions you can use

  1. Add a new prop called hidden to the FormEntity component and just hide the contents of the component while keeping all the hooks.
function FormEntity(props) {
  // place all your hooks and logic here
  
  if (props.hidden) {
    return null;
  }

  // return the JSX if this element is not hidden
}
  1. Instead of passing formStructure by calling the method, check if formStructure={<FormStructure />} works

Leave a Comment