Home > database >  Why am I receiving 'Invalid hook call' error?
Why am I receiving 'Invalid hook call' error?

Time:10-02

I am dispatching a redux action on a button click. When the button is clicked I then receive the error 'Invalid hook call. Hooks can only be called inside of the body of a function component.' I can have this same action be dispatched on the page render and it works fine. I have other actions being dispatched onClick but this one isn't working. This is the action

import axios from "axios";

export const addReview = (productInfo) => async (dispatch) => {

  try {
    await axios({
      method: "post",
      url: url,
      header: { "Content-Type": "application/json" },
      data: {
        name: productInfo.name,
        rating: productInfo.rating,
        description: productInfo.description,
        buyAgain: productInfo.buyAgain,
        productId: productInfo.productId,
      },
    }).then((response) => {
      dispatch({
        type: "PRODUCTS_SUCCESS",
        payload: response.data,
      });
    });
  } catch (error) {
    dispatch({
      type: "PRODUCTS_FAILURE",
      payload: error,
    });
  }
};

And here is the action dispatch on onClick event

  const submitReview = () => {
      let productInfo = {
        name,
        rating: starRate,
        description: message,
        buyAgain,
        productId: currentProduct,
      };
      dispatch(addReview(productInfo));
  };

Here is the entire component

import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import "./ProductReview.css";
import { ImStarEmpty } from "react-icons/im";
import { ImStarFull } from "react-icons/im";
import { IoPersonCircle } from "react-icons/io5";
import { addReview } from "../../actions/addReview";

export const ProductReview = () => {
  const [starRate, setStarRate] = useState();
  const [message, setMessage] = useState();
  const [buyAgain, setBuyAgain] = useState(true);
  const [missingRating, setMissingRating] = useState(false);

  const dispatch = useDispatch();

  const fName = localStorage.getItem("firstName");

  const currentProduct = useSelector(
    (state) => state.userInfoReducer.currentProductReview
  );

  const allProducts = useSelector((state) => state.productReducer.data);

  let productToDisplay;
  let title;

  let name;

  allProducts.map((product) => {
    if (product._id === currentProduct) {
      productToDisplay = product;
      if (product.title.length > 60) {
        title = product.title.substring(0, 60)   "...";
      }
    }
    return productToDisplay;
  });

  const submitReview = () => {
    let productInfo = {
      name,
      rating: starRate,
      description: message,
      buyAgain,
      productId: currentProduct,
    };
    dispatch(addReview(productInfo));
  };

  return (
    <>
      <div className="product-review-container">
        <h1 className="product-review-title">Create Review</h1>
        <div className="product-review-product">
          <img
            className="review-product-img"
            src={productToDisplay.image}
            alt="product"
          />
          <h2 className="review-product-title">{title}</h2>
        </div>
        <hr className="line-break" />
        <div className="review-product-review">
          <div className="review-product-review-container">
            <h2 className="review-product-review-title">Overall Rating</h2>
            <p className="review-product-review-stars">
              {starRate >= 1 ? (
                <ImStarFull
                  className="star-full"
                  onClick={() => setStarRate(1)}
                />
              ) : (
                <ImStarEmpty
                  className="star-empty"
                  onClick={() => setStarRate(1)}
                />
              )}
              {starRate >= 2 ? (
                <ImStarFull
                  className="star-full"
                  onClick={() => setStarRate(2)}
                />
              ) : (
                <ImStarEmpty
                  className="star-empty"
                  onClick={() => setStarRate(2)}
                />
              )}
              {starRate >= 3 ? (
                <ImStarFull
                  className="star-full"
                  onClick={() => setStarRate(3)}
                />
              ) : (
                <ImStarEmpty
                  className="star-empty"
                  onClick={() => setStarRate(3)}
                />
              )}
              {starRate >= 4 ? (
                <ImStarFull
                  className="star-full"
                  onClick={() => setStarRate(4)}
                />
              ) : (
                <ImStarEmpty
                  className="star-empty"
                  onClick={() => setStarRate(4)}
                />
              )}
              {starRate >= 5 ? (
                <ImStarFull
                  className="star-full"
                  onClick={() => setStarRate(5)}
                />
              ) : (
                <ImStarEmpty
                  className="star-empty"
                  onClick={() => setStarRate(5)}
                />
              )}
            </p>
            {missingRating ? (
              <p className="missing-rating">! Must choose a rating</p>
            ) : (
              ""
            )}
          </div>
          <button
            className="review-product-review-clear"
            onClick={() => setStarRate(0)}
          >
            Clear
          </button>
        </div>
        <div className="review-product-buy-again">
          <h2 className="review-product-buy-again-title">
            Would you buy this item again?
          </h2>
          <div className="btn-group btn-group-toggle" data-toggle="buttons">
            <label className="btn btn-secondary active">
              <input
                type="radio"
                name="options"
                id="option1"
                autoComplete="off"
                onClick={() => setBuyAgain(true)}
              />{" "}
              Yes
            </label>
            <label className="btn btn-secondary">
              <input
                type="radio"
                name="options"
                id="option2"
                autoComplete="off"
                onClick={() => setBuyAgain(false)}
              />{" "}
              No
            </label>
          </div>
        </div>
        <hr className="line-break" />
        <div className="review-product-message">
          <h2 className="review-product-message-title">
            Add a written message
          </h2>
          <input
            type="text"
            className="form-control review-product-message-input"
            placeholder="What did you like or dislike?"
            onChange={(e) => setMessage(e.target.value)}
          />
          <h2 className="review-product-name-title">Choose your public name</h2>
          <p className="review-product-appear">
            This is how you'll appear to other customers
          </p>
          <div className="review-product-user-info">
            <IoPersonCircle className="review-product-profile" />
            <input
              type="text"
              className="form-control review-product-profile-name"
              defaultValue={fName}
              onChange={(e) => (name = e.target.value)}
            />
          </div>
          <hr className="line-break" />
          <button
            className="review-product-submit"
            onClick={() => submitReview()}
          >
            Submit
          </button>
          {missingRating ? (
            <p className="missing-rating">Missing information</p>
          ) : (
            ""
          )}
        </div>
      </div>
    </>
  );
};

CodePudding user response:

Out of sync versions of React and React DOM

You can run npm ls react-dom in your react projet and if the version is less then 16.8.0 it does not yet support Hooks

2- Do not call Hooks in event handlers.

CodePudding user response:

Your hooks must always be at the top of your functional Component. You may be able to resolve this by simply moving down the initialization of ‘fName’ below your hooks.

  • Related