Next.js Discord

Discord Forum

Error: useState and useMemo is called conditionally.ct Hooks must be called in the exact same order

Unanswered
Jenn posted this in #help-forum
Open in Discord
Avatar
JennOP
const OrderComponent: React.FC<OrderComponentProps> = ({
  error,
  waterTypes,
  refillingStation,
}) => {
  if (error) {
    return <div>Error: {error.message}</div>;
  }

  if (!waterTypes || !refillingStation) {
    return <div>Loading...</div>;
  }

  const [user, setUser] = useState<User>({
    firstName: '',
    //
  })

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setUser((prevUser) => ({
      ...prevUser,
      [name]: value,
    }));
  };

  const [cart, setCart] = useState< WaterTypeQty[]>([])

  const total = useMemo<number>(() => {
    const calculateTotal = () => {
      return cart.reduce((total, item) => total + item.price * item.quantity, 0);
    };

    return calculateTotal();
  }, [cart])

1 Reply

Avatar
Toyger
hooks should be before any conditional return, it described here https://legacy.reactjs.org/docs/hooks-rules.html
if order of hook execution changes between renders it break logic.
by early return you break order, so you need put your hooks run before, below is corrected code.
either you make your conditions on level higher where you passing your props, and check them before render this component.
const OrderComponent: React.FC<OrderComponentProps> = ({
  error,
  waterTypes,
  refillingStation,
}) => {
  const [user, setUser] = useState<User>({
    firstName: '',
    //
  })
  const [cart, setCart] = useState< WaterTypeQty[]>([])

  const total = useMemo<number>(() => {
    const calculateTotal = () => {
      return cart.reduce((total, item) => total + item.price * item.quantity, 0);
    };

    return calculateTotal();
  }, [cart])

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  if (!waterTypes || !refillingStation) {
    return <div>Loading...</div>;
  }

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setUser((prevUser) => ({
      ...prevUser,
      [name]: value,
    }));
  };