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.