reactJS doesn't render the data after being fetched in useEffect hook - reactjs

I have an API using axios which will fetch a list of products. I have called it inside useEffect() hook with [] as the second argument. I check the data before running into return command of the functional component and the data is there, but my ProductCard component are not rendered. Could you please help me with this?
FeaturedProducts.js
const FeaturedProducts = (props) => {
const [productList, setProductList] = useState([]);
// const productList = useSelector(productListSelector);
useEffect(async () => {
let rs = await fetchFeaturedProducts();
setProductList(rs);
}, []);
console.log(productList);
return (
<div className="container-fluid pt-5">
<div className="text-center mb-4">
<h2 className="section-title px-5"><span className="px-2">Featured</span></h2>
</div>
<div className="row px-xl-5 pb-3">
{
productList.map(product => {
<ProductCard id={product.id} name={product.name} price={product.price} img="abc"/> // need img={product.img}
})
}
</div>
</div>
);
}
export default FeaturedProducts;
fetchFeaturedProducts()
const fetchFeaturedProducts = async () => {
try {
let response = await AxiosClient.post("/product/search", {
searchType: "desc",
searchBy: "average_rating"
});
let data = response.data.data;
return data;
}
catch (error) {
raiseErrorMessages(error.response.data.errors);
return [];
}
}
ProductCard.js
const ProductCard = (props) => {
const detailUrl = "/product?id=" + toString(props.id);
return (
<div className="col-lg-3 col-md-6 col-sm-12 pb-1">
<div className="card product-item border-0 mb-4">
<div className="card-header product-img position-relative overflow-hidden bg-transparent border p-0">
<img className="img-fluid w-100" src={props.img} alt="" />
</div>
<div className="card-body border-left border-right text-center p-0 pt-4 pb-3">
<h6 className="text-truncate mb-3">{props.name}</h6>
<div className="d-flex justify-content-center">
<h6>{props.price}</h6>
{/* <h6 className="text-muted ml-2"><del>{props.price}</del></h6> */}
</div>
</div>
<div className="card-footer d-flex justify-content-between bg-light border">
<i className="fas fa-eye text-primary mr-1" />View Detail
<i className="fas fa-shopping-cart text-primary mr-1" />Add To Cart
</div>
</div>
</div>
)
}
export default ProductCard;
Thank you for your precious time.

that happens because you missed a return when you mapped your data
// .....
{
productList.map(product => {
return (<ProductCard id={product.id} name={product.name}
price={product.price} img="abc"/>) // need img=
{product.img}
})
}
or you can use ()
{
productList.map(product => (
<ProductCard id={product.id} name={product.name}
price={product.price} img="abc"/>) // need img=
{product.img})
}

Related

how to get product id from sanity.io for every button that clicked in reactjs?

i'm trying to make product detail page but i am struggle to solve this problem. I fetch products from sanity.io and return the data using map. i want whenever i click the detail btn it's direct me to detail page and get the information of product
here is my code
const Products = () => {
const [allProducts, setAllProducts] = useState([]);
useEffect(() => {
async function getAllProducts() {
const allProduct = '*[_type == "product"]';
const response = await client.fetch(allProduct);
setAllProducts(await response);
}
getAllProducts();
}, []);
return (
<>
<Navbar />
<div className="container-fluid ">
<div className="row g-3 mb-5 ">
{allProducts.map((product) => {
console.log(product);
return (
<div className="col-12 col-md-3" key={product._id}>
<div className="card">
<img
src={urlFor(product.image[0])}
className="card-img-top"
alt={product.name}
/>
<div className="card-body">
<h1>${product.price}</h1>
<h5 className="card-title">{product.name} </h5>
<h6>{product.detail}</h6>
<div>
<Link to="/detail" className="btn btn-danger m-2">
Detail
</Link>
</div>
</div>
</div>
</div>
);
})}
</div>
<Footer />
</div>
</>
);
};

React Router Dom Issue - localhost:3000/products to localhost:3000/products/2 is working fine but when I refresh page then I get some error in console

I have an Index.js where I am getting data via async function and sending data to ProductList Component and from ProductList Component I am sending an id onclick of product Image and then I get Product Detail Page a Single Product. I get Single Product with no issues but when I refresh then I get an error in console saying - Cannot read properties of undefined (reading 'filter') I have used REDUX.
INDEX.JS
import React, { useState, useEffect } from "react";
// import PageNotFound from "../other/page-not-found/PageNotFound";
import ProductList from "./product-list/ProductList";
import ProductFilter from "./product-filter/ProductFilter";
import ProductSearch from "./product-search/ProductSearch";
import Cart from "./cart/Cart";
// import MobileFilterModal from "./mobile-filter-modal/MobileFilterModal";
import { useDispatch, useSelector } from "react-redux";
import { setProductListData } from "../../redux/actions/product-list-actions/ProductListActions";
import Loading from "../other/loading/Loading";
import Skeleton from "../other/skeletons/Skeleton";
// import PageNotFound from "../other/page-not-found/PageNotFound";
const Index = () => {
const [isLoading, setIsLoading] = useState(true);
const dispatch = useDispatch();
const filters = useSelector(
(state) => state.productList.productListData.filters
);
const products = useSelector(
(state) => state.productList.productListData.products
);
// console.log(products);
useEffect(() => {
getProductsData();
}, []);
const getProductsData = async () => {
try {
const response = await fetch("http://localhost:8000/productListing");
const data = await response.json();
if (data) {
setIsLoading(false);
dispatch(setProductListData(data));
} else {
setIsLoading(true);
}
} catch (error) {
console.log(error);
}
};
// if (isLoading) {
// return <Loading loadingProductList={"products"} />;
// }
return (
<>
{filters && products && (
<main>
<div className="dvMain">
<div className="container-fluid">
<div className="row">
<div className="dvFilters col-lg-3 col-xl-2 d-none d-lg-block text-right">
<div className="sticky-top" style={{ top: "90px" }}>
{isLoading
? filters.map((filter) => {
return (
<Skeleton key={filter.id} filterId={filter.id} />
);
})
: filters &&
filters.map((filter) => {
return <ProductFilter key={filter.id} {...filter} />;
})}
</div>
</div>
<div className="dvProducts col-lg-6 col-xl-8">
<div className="row">
<div className="dvSearch col-sm-9 col-md-10 col-lg-12 mb-3">
{isLoading ? (
<Skeleton productSearch={"productSearch"} />
) : (
<ProductSearch />
)}
</div>
<div className="dvFilterMobile col-sm-3 col-md-2 d-lg-none text-center text-sm-right mb-3">
<a
href=""
className="btn bg-light w-100"
data-toggle="modal"
data-target="#mobileFiltersModal"
>
<i className="fa fa-filter"></i>Filter
</a>
</div>
</div>
<div className="row">
{isLoading
? products.map((product) => {
return (
<Skeleton key={product.id} productId={product.id} />
);
})
: products &&
products.map((product) => {
return <ProductList key={product.id} {...product} />;
})}
</div>
</div>
{isLoading ? <Skeleton cart={"cart"} /> : <Cart />}
</div>
</div>
</div>
</main>
)}
</>
);
};
export default Index;
PRODUCTLIST.JS
import React from "react";
import { Link } from "react-router-dom";
import "./productlist.css";
const ProductList = ({ id, img, pack, price, size, heading, description }) => {
// console.log(id, img, pack, price, size, heading, description);
// console.log(id, img, pack, price, size, heading, description);
return (
<>
<div className="col-6 col-md-4 col-lg-6 col-xl-3 mb-4">
<div className="border border-light shadow-sm p-1 h-100">
<div className="image">
<p className="packs">pack of 5</p>
<div className="bg-light text-center pt-2 pb-2 mb-1">
<Link className="d-inline-block" to={`/${id}`}>
<img src={img} className="img-fluid" alt={heading} />
</Link>
</div>
<h5 className="heading-5 text-center">{heading}</h5>
</div>
<div className="description d-flex justify-content-between mb-1">
<div className="paragraph">
<p>{size}</p>
</div>
<div className="paragraph mr-2">
<span>
<i className="fa fa-inr"></i>
<span>{price}</span>
</span>
</div>
</div>
<div className="addBtn text-center">
<button className="btn btn-white w-100" href="detail.html">
Add to Bag
</button>
</div>
</div>
</div>
</>
);
};
export default ProductList;
PRODUCTDETAIL.JS
import React, { useState, useEffect } from "react";
import Cart from "../cart/Cart";
import "./productdetail.css";
import Loading from "../../other/loading/Loading";
import PageNotFound from "../../other/page-not-found/PageNotFound";
import { Link, useParams } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { setSelectedProduct } from "../../../redux/actions/product-detail-actions/ProductDetailActions";
const ProductDetail = () => {
const [isLoading, setIsLoading] = useState(true);
const { id } = useParams();
const dispatch = useDispatch();
const products = useSelector(
(state) => state.productList.productListData.products
);
const singleProduct = useSelector(
(state) => state.singleProduct.singleProduct
);
// console.log(id);
// console.log(products);
// console.log(singleProduct);
useEffect(() => {
getProductId();
}, []);
const getProductId = () => {
window.scrollTo(0, 0);
// localStorage.setItem("setProducts", JSON.stringify(products));
// let getProducts = JSON.parse(localStorage.getItem("setProducts"));
products ? setIsLoading(false) : setIsLoading(true);
let singleProduct = [];
singleProduct = products.filter((product) => {
return product.id === parseInt(id);
});
// console.log(singleProduct);
dispatch(setSelectedProduct(singleProduct));
};
if (isLoading) {
return <Loading loadingProductDetail={"product detail"} />;
}
return (
<>
{singleProduct && (
<main>
<div className="dvMain">
<div className="container">
<div className="row">
{singleProduct ? (
singleProduct.map((product) => {
const {
id,
img,
heading,
description,
category,
price,
size,
} = product;
return (
<div key={id} className="col-lg-9">
<div className="row">
<div className="dvProducts col-12">
<div className="row">
<div className="dvBack col-12">
<Link
to="/products"
className="mb-1 d-inline-block"
>
<i className="fa fa-angle-left"></i>
<span> Back</span>
</Link>
</div>
<div className="col-12">
<div className="row">
<div className="col-12 col-md-6 col-xl-4 mb-3">
<div className="border border-light shadow-sm p-1 h-100 d-flex justify-content-center align-items-center">
<div className="bg-light text-center h-100">
<a className="d-inline-block h-100">
<img
src={img}
className="img-fluid"
alt={heading}
/>
</a>
</div>
</div>
</div>
<div className="col-12 col-md-6 col-xl-8 d-flex mb-3 mb-xl-0">
<div className="m-md-auto">
<div>
<h2 className="heading-2">{heading}</h2>
</div>
<div className="mb-2">
<i className="fa fa-star text-warning d-inline-block"></i>
<i className="fa fa-star text-warning d-inline-block"></i>
<i className="fa fa-star text-warning d-inline-block"></i>
<i className="fa fa-star-o text-warning d-inline-block"></i>
<i className="fa fa-star-o text-warning d-inline-block"></i>
</div>
<div className="mb-3">
<p className="paragraph">
{description}
</p>
</div>
<div className="d-flex mb-3">
<div className="mr-2">
<h5 className="heading-5 d-inline-block mb-1 mr-1">
Size:
</h5>
<span className="paragraph d-inline-block">
{size}
</span>
</div>
<div className="mr-2 ml-2">
<h5 className="heading-5 d-inline-block mb-1 mr-1">
Category:
</h5>
<span className="paragraph d-inline-block">
{category}
</span>
</div>
<div className="ml-2">
<h5 className="heading-5 d-inline-block mb-1 mr-1">
Price:
</h5>
<span className="paragraph d-inline-block">
<i className="fa fa-inr"></i>
<span className="d-inline-block">
{price}.00
</span>
</span>
</div>
</div>
<div>
<button
className="btn btn-black"
href="/-"
>
Add to Bag
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div className="dvTabs col-12 mt-4">
<div className="row">
<div className="col-12">
<div className="row">
<div className="col-12">
<ul
className="nav nav-tabs"
id="myTab"
role="tablist"
>
<li className="nav-item">
<a
className="heading-5 nav-link active"
id="description-tab"
data-toggle="tab"
href="#description"
role="tab"
aria-controls="description"
aria-selected="true"
>
Description
</a>
</li>
</ul>
</div>
</div>
<div
className="dvTabsContent tab-content row"
id="myTabContent"
>
<div
className="tab-pane fade show active py-3 col-12"
id="description"
role="tabpanel"
aria-labelledby="description-tab"
>
<div className="row">
<div className="col-12">
<div className="table-responsive">
<table className="table table-striped table-bordered">
<thead>
<tr>
<th
className="heading-5"
scope="col"
>
Details
</th>
<th
className="heading-5"
scope="col"
>
Weight
</th>
<th
className="heading-5"
scope="col"
>
1Glass 250ml
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Amount per serving</td>
<td>250ml</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
})
) : (
<PageNotFound />
)}
<Cart id={id} />
</div>
</div>
</div>
</main>
)}
</>
);
};
export default ProductDetail;
APP.JS
import React from "react";
import "./App.css";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Homepage from "./containers/home-page/index";
import Header from "./containers/header/Header";
import Footer from "./containers/footer/Footer";
// import ProductListing from "./containers/products/product-list/ProductList";
import ProductDetail from "./containers/products/product-detail/ProductDetail";
import PageNotFound from "./containers/other/page-not-found/PageNotFound";
import Account from "./containers/account/index";
import Checkout from "./containers/checkout/Index";
import LoginModal from "./containers/auth/modal/login-modal/LoginModal";
import SignupModal from "./containers/auth/modal/signup-modal/SignupModal";
import MobileCartModal from "./containers/products/mobile-cart-modal/MobileCartModal";
import Index from "./containers/products";
const App = () => {
return (
<>
<BrowserRouter>
<Header />
<main>
<Switch>
<Route path="/" exact component={Homepage} />
<Route path="/products" component={Index} />
<Route path="/:id" children={<ProductDetail />} />
<Route path="/checkout" component={Checkout} />
<Route path="/account" component={Account} />
<Route path="*" component={PageNotFound} />
</Switch>
</main>
<Footer />
<LoginModal />
<SignupModal />
<MobileCartModal />
</BrowserRouter>
</>
);
};
export default App;
PRODUCTDETAILACTIONS.JS
export const setSelectedProduct = (product) => {
return {
type: "SET_SELECTED_PRODUCT",
payload: product,
};
};
PRODUCTDETAILREDUCER.JS
const initialState = {
singleProduct: [],
};
export const productDetailReducer = (state = initialState, action) => {
switch (action.type) {
case "SET_SELECTED_PRODUCT":
return {
...state,
singleProduct: action.payload,
};
default:
return state;
}
};
REDUCER INDEX.JS
import { combineReducers } from "redux";
import { aboutUsReducer } from "./aboutus-reducers/AboutUsReducers";
import { footerReducer } from "./footer-reducers/FooterReducer";
import { headerReducer } from "./header-reducers/headerReducer";
import { mainSliderReducer } from "./homepage-reducers/mainSliderReducer";
import { youtubeReducer } from "./homepage-reducers/YoutubeReducer";
import { productDetailReducer } from "./product-detail/ProductDetailReducer";
import { productListReducer } from "./product-list-reducers/ProductListReducer";
import { productSliderData } from "./productslider-reducers/ProductSliderReducer";
import { teamReducer } from "./team-reducers/TeamReducers";
const rootReducer = combineReducers({
header: headerReducer,
mainSlider: mainSliderReducer,
youtube: youtubeReducer,
aboutUs: aboutUsReducer,
productSlider: productSliderData,
team: teamReducer,
footer: footerReducer,
productList: productListReducer,
singleProduct: productDetailReducer,
});
export default rootReducer;
STORE.JS
import { createStore } from "redux";
import rootReducer from "./reducers/index";
const store = createStore(
rootReducer,
{},
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
PRODUCTLISTACTIONS.JS
export const setProductListData = (data) => {
return {
type: "SET_PRODUCT_LIST_DATA",
payload: data,
};
};
PRODUCTLISTREDUCER.JS
const initialState = {
productListData: {},
};
export const productListReducer = (state = initialState, action) => {
switch (action.type) {
case "SET_PRODUCT_LIST_DATA":
return {
...state,
productListData: action.payload,
};
default:
return state;
}
};
DB.JSON
I am fetching productListing from json file which look like this
From what I can see, the ProductDetails component is missing a dependency on the id route param to select the correct products to filter, and I am assuming that since the index file is fetching and populating the store each time it mounts that perhaps the initial products state is null, undefined, or some other non-array value that is missing the filter method.
Add the missing id route param to the useEffect hook's dependency array so products are fetched when the route changes.
const { id } = useParams();
...
useEffect(() => {
getProductId(id);
}, [id]);
Since it appears that products is potentially undefined, i.e. falsey, you should account for then when attempting to filter the array. Provide a fallback empty array to filter from if products is falsey.
const getProductId = (id) => {
window.scrollTo(0, 0);
products ? setIsLoading(false) : setIsLoading(true);
const singleProduct = (products || []).filter((product) => {
return product.id === parseInt(id);
});
dispatch(setSelectedProduct(singleProduct));
};
You didn't include any of your Redux code (i.e. actions/reducers/store configuration) but a suggestion is to always provide a consistent state invariant. For example, always returning an array of products and using the array length and some "isLoading" state to determine when it's safe to filter the state. At a minimum, if it's guaranteed that products is always an array then all array methods will always work as expected, regardless of where the app is in its lifecycle and loading data/state.
Hey I found this solution. I fetched products through async function instead of getting products back from useSelector. Please check ProductDetail Component below.
PRODUCTDETAIL.JS
import React, { useState, useEffect } from "react";
import Cart from "../cart/Cart";
import "./productdetail.css";
import Loading from "../../other/loading/Loading";
import PageNotFound from "../../other/page-not-found/PageNotFound";
import { Link, useParams } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { setSelectedProduct } from "../../../redux/actions/product-detail-actions/ProductDetailActions";
const ProductDetail = () => {
const [loading, setLoading] = useState(true);
const { id } = useParams();
const dispatch = useDispatch();
const singleProduct = useSelector(
(state) => state.singleProduct.singleProduct
);
useEffect(() => {
getProductListingData();
}, []);
const getProductListingData = async () => {
try {
const response = await fetch("http://localhost:8000/productListing");
const data = await response.json();
if (data) {
setLoading(false);
getProductID(data.products);
} else {
// setProducts("PRODUCT LISTING DATA NOT FOUND");
setLoading(true);
console.log("PRODUCT LISTING DATA NOT FOUND");
}
} catch (error) {
console.log(error);
}
};
const getProductID = (products) => {
window.scroll(0, 0);
let singleProduct = [];
singleProduct = products.filter((item) => {
return item.id === parseInt(id);
});
dispatch(setSelectedProduct(singleProduct));
};
if (loading) {
return <Loading loadingProductDetail="Loading Product Details" />;
}
console.log(id);
console.log(singleProduct);
return (
<>
<main>
<div className="dvMain">
<div className="container">
<div className="row">
{singleProduct.length > 0 &&
singleProduct.map((product) => {
const {
id,
img,
heading,
description,
pack,
price,
size,
category,
detailsTable,
} = product;
return (
<div key={id} className="col-lg-9">
<div className="row">
<div key={id} className="dvProducts col-12">
<div className="row">
<div className="dvBack col-12">
<Link
to="/products"
className="mb-1 d-inline-block"
>
<i className="fa fa-angle-left"></i>
<span> Back</span>
</Link>
</div>
<div className="col-12">
<div className="row">
<div className="col-12 col-md-6 col-xl-4 mb-3">
<div className="border border-light shadow-sm p-1 h-100 d-flex justify-content-center align-items-center">
<div className="bg-light text-center h-100">
<a className="d-inline-block h-100">
<img
src={img}
className="img-fluid"
alt={heading}
/>
</a>
</div>
</div>
</div>
<div className="col-12 col-md-6 col-xl-8 d-flex mb-3 mb-xl-0">
<div className="m-md-auto">
<div>
<h2 className="heading-2">{heading}</h2>
</div>
<div className="mb-2">
<i className="fa fa-star text-warning d-inline-block"></i>
<i className="fa fa-star text-warning d-inline-block"></i>
<i className="fa fa-star text-warning d-inline-block"></i>
<i className="fa fa-star-o text-warning d-inline-block"></i>
<i className="fa fa-star-o text-warning d-inline-block"></i>
</div>
<div className="mb-3">
<p className="paragraph">{description}</p>
</div>
<div className="d-flex mb-3">
<div className="mr-2">
<h5 className="heading-5 d-inline-block mb-1 mr-1">
Pack:
</h5>
<span className="paragraph d-inline-block">
{pack}
</span>
</div>
<div className="mr-2">
<h5 className="heading-5 d-inline-block mb-1 mr-1">
Size:
</h5>
<span className="paragraph d-inline-block">
{size}
</span>
</div>
<div className="mr-2 ml-2">
<h5 className="heading-5 d-inline-block mb-1 mr-1">
Category:
</h5>
<span className="paragraph d-inline-block">
{category}
</span>
</div>
<div className="ml-2">
<h5 className="heading-5 d-inline-block mb-1 mr-1">
Price:
</h5>
<span className="paragraph d-inline-block">
<i className="fa fa-inr"></i>
<span className="d-inline-block">
{price}.00
</span>
</span>
</div>
</div>
<div>
<button
className="btn btn-black"
href="/-"
>
Add to Bag
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div className="dvTabs col-12 mt-4">
<div className="row">
<div className="col-12">
<div className="row">
<div className="col-12">
<ul
className="nav nav-tabs"
id="myTab"
role="tablist"
>
<li className="nav-item">
<a
className="heading-5 nav-link active"
id="description-tab"
data-toggle="tab"
href="#description"
role="tab"
aria-controls="description"
aria-selected="true"
>
Description
</a>
</li>
</ul>
</div>
</div>
<div
className="dvTabsContent tab-content row"
id="myTabContent"
>
<div
className="tab-pane fade show active py-3 col-12"
id="description"
role="tabpanel"
aria-labelledby="description-tab"
>
<div className="row">
<div className="col-12">
<div className="table-responsive">
<table className="table table-striped table-bordered">
<thead>
<tr>
<th
className="heading-5"
scope="col"
>
Details
</th>
<th
className="heading-5"
scope="col"
>
Weight
</th>
<th
className="heading-5"
scope="col"
>
1Glass 250ml
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Amount per serving</td>
<td>250ml</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
})}
<Cart id={id} />
</div>
</div>
</div>
</main>
</>
);
};
export default ProductDetail;

How to open one dropdown item?

friends, I have array of questions, and a dropdown list for them... i want to open any question, but all questions are opening together... please help
const FAQ = () => {
const [isOpenAnswer, setIsOpenAnswer] = useState(false)
const toggle = (id) => {
questions.forEach((q) => {
if(q.id === id){
setIsOpenAnswer((prevState) => !prevState)
}
})
}
return <Layout>
<div className="questionsBox pb-5">
<h2 className="title pt-4 pb-4" >Frequently Asked Questions</h2>
{questions.map((q, index) => {
return <div className="question pl-1 pt-3 pb-3 pr-1" key={index}>
<div className="d-flex justify-content-between">
<span className="questionTitle">{q.question}</span>
<img className="questionIcon"
src={Plus} alt="plus"
onClick={() => toggle(q.id)}
/>
</div>
{isOpenAnswer && <p className="answer pt-2 pb-2">
{q.answer}
{q.source}
</p>}
</div>
})}
</div>
</Layout>
}
Use a Javascript object to track which unique q.id is being set to true.
const FAQ = () => {
const [isOpenAnswer, setIsOpenAnswer] = useState({})
const toggle = (id) => {
setIsOpenAnswer(prevState => ({
...prevState,
[id]: !prevState[id],
});
}
return <Layout>
<div className="questionsBox pb-5">
<h2 className="title pt-4 pb-4" >Frequently Asked Questions</h2>
{questions.map((q, index) => {
return <div className="question pl-1 pt-3 pb-3 pr-1" key={index}>
<div className="d-flex justify-content-between">
<span className="questionTitle">{q.question}</span>
<img className="questionIcon"
src={Plus} alt="plus"
onClick={() => toggle(q.id)}
/>
</div>
{isOpenAnswer[q.id] && <p className="answer pt-2 pb-2">
{q.answer}
{q.source}
</p>}
</div>
})}
</div>
</Layout>
}
You're using the same prop for all of them here:
{isOpenAnswer && <p className="answer pt-2 pb-2">
{q.answer}
{q.source}
</p>}
Try saving something unique in state to identify what you're supposed to be showing, e.g.,
{selectedQuestionId && /* the rest */ }
and set the selectedQuestionId where you're currently setting isOpenAnswer .

Irregular status 500 on Apollo GraphQL requests ("Variable X of required type \"String!\" was not provided.")

I get a network error just some of the times I call a lazyQuery in my React project with Apollo and GraphQL. In my app I can successfully get search results back containing various drinks. These are mapped to their own buttons. On clicking one of these buttons a function triggers that queries data about that specific drink.
Most of the time it works fine, but some of the times I get a status 500 error, saying that the cocktailName variable of the type string was not provided. When I've tried requesting the same drinks several times I've sometimes gotten a good response and sometimes not. When I try to debug the function that sends the query, it seems to receive the variable every single time, and I haven't managed to crash the app when I have debug breakpoints in that function. I'd really appreciate any assistance with this!
Main component for the drinks:
import React from 'react';
import { useStoreState, useStoreActions } from 'easy-peasy';
import { useQuery, useLazyQuery } from '#apollo/react-hooks';
import { RANDOM_COCKTAIL_QUERY, ONE_COCKTAIL_BY_NAME_QUERY } from './Queries';
import LoadingSpinner from './LoadingSpinner';
import CocktailSearchFieldAlt from './CocktailSearchFieldAlt';
import { cocktailIngredientList, cocktailMeasureList } from './ingredientLists';
// Imported ingredient and measure lists represent names of keys in objects
export default function Cocktails() {
const dataType = useStoreState((state) => state.cocktailDataType);
const changeDataType = useStoreActions((actions) => actions.changeCocktailDataType);
const dataSet = useStoreState((state) => state.cocktailDataSet);
const changeDataSet = useStoreActions((actions) => actions.changeCocktailDataSet);
const randomizeClick = () => {
if (dataType !== 'randomCocktail') {
changeDataType('randomCocktail');
changeDataSet('data');
}
refetch();
};
const specifikLookup = (drinkName) => {
if (dataType === 'randomCocktail') {
changeDataSet('specificData');
changeDataType('oneCocktailByName');
}
getDrink({ variables: { cocktailName: drinkName } });
};
const { loading, error, data, refetch } = useQuery(RANDOM_COCKTAIL_QUERY);
const [
getDrink,
{ data: specificData, loading: loadingSpecific, error: errorSpecific }
] = useLazyQuery(ONE_COCKTAIL_BY_NAME_QUERY);
if (loading || loadingSpecific) return <LoadingSpinner />;
if (error) return `Error! ${error}`;
if (errorSpecific) return `Error! ${errorSpecific}`;
return (
<section id="cocktail">
<h2 className="mainHeader mt-5 mb-4 scrollTrick">Cocktail</h2>
<div className="card card-body mb-3">
<div className="row">
<div className="col-md-9">
<h4>{eval(dataSet)[dataType].strDrink}</h4>
<p>
{eval(dataSet)[dataType].strAlcoholic}
<br />
Best served in: {eval(dataSet)[dataType].strGlass}
</p>
<h6>Recipee</h6>
<div className="row">
<div className="col-md-4">
<ul>
{eval(dataSet) &&
cocktailIngredientList.map(
(x, i) =>
eval(dataSet)[dataType][x] && (
<li key={i}>{eval(dataSet)[dataType][x]}</li>
)
)}
</ul>
</div>
<div className="col-md-3">
<ul>
{data &&
cocktailMeasureList.map(
(x) => eval(dataSet)[dataType][x] && <li>{eval(dataSet)[dataType][x]}</li>
)}
</ul>
</div>
</div>
<h6>Instructions</h6>
<p>{eval(dataSet)[dataType].strInstructions}</p>
</div>
<div className="col-md-3">
<img src={eval(dataSet)[dataType].strDrinkThumb} alt="Cocktail" className="img-fluid mb-3" />
<button className="btn btn-primary btn-block" onClick={() => randomizeClick()}>
Randomize Cocktail
</button>
</div>
</div>
{/* <CocktailSearchField specificLookup={specifikLookup} /> */}
<CocktailSearchFieldAlt specificLookup={specifikLookup} />
</div>
</section>
);
}
Search component for the drinks:
import React, { useState } from 'react';
import { useStoreState, useStoreActions } from 'easy-peasy';
import { useLazyQuery } from '#apollo/react-hooks';
import { MULTIPLE_COCKTAILS_BY_NAME_QUERY, MULTIPLE_COCKTAILS_BY_INGREDIENT_QUERY } from './Queries';
import LoadingSpinner from './LoadingSpinner';
export default function SearchField(props) {
const [
searchInput,
setSearchInput
] = useState('');
const drinkOptionValue = useStoreState((state) => state.drinkOptionValue);
const changeDrinkOptionValue = useStoreActions((actions) => actions.changeDrinkOptionValue);
const handleSubmit = (e) => {
e.preventDefault();
getDrinks({ variables: { cocktailName: searchInput } });
getDrinksByIngredient({ variables: { ingredientName: searchInput } });
};
const [
getDrinks,
{ loading, data }
] = useLazyQuery(MULTIPLE_COCKTAILS_BY_NAME_QUERY);
const [
getDrinksByIngredient,
{ loading: loadingByIngredient, data: dataByIngredient }
] = useLazyQuery(MULTIPLE_COCKTAILS_BY_INGREDIENT_QUERY);
const lookupDrink = (e) => {
console.log(e.target.value);
changeDrinkOptionValue(e.target.value);
props.specificLookup(e.target.value);
};
if (loading || loadingByIngredient) return <LoadingSpinner />;
return (
<div>
<div className="row border-top mt-3 justify-content-center">
<div className="mt-3">
<form className="form-inline my-2 my-lg-0" onSubmit={handleSubmit}>
<input
className="form-control mr-sm-1"
type="text"
placeholder="Search cocktail"
onChange={(e) => setSearchInput(e.target.value)}
value={searchInput}
/>
<button className="btn btn-secondary my-2 my-sm-0 pl-3 pt-2 pb-2 pr-3" type="submit">
<i className="material-icons mt-2">search</i>
</button>
</form>
</div>
</div>
{data &&
(data.multipleCocktailsByName !== null ? (
<div className="container">
<div className="row">
<div className="col">
<h6 className="mt-3 mb-3 text-center">Search results: </h6>
</div>
</div>
<div className="row justify-content-center">
{dataByIngredient.multipleCocktailsByIngredient !== null &&
dataByIngredient.multipleCocktailsByIngredient.map((cocktailByIngredient) => {
if (
data.multipleCocktailsByName.some(
(cocktail) => cocktail.strDrink === cocktailByIngredient.strDrink
)
) {
return;
} else {
data.multipleCocktailsByName.push(cocktailByIngredient);
}
})}
{
(data.multipleCocktailsByName.sort(
(a, b) => (a.strDrink > b.strDrink ? 1 : b.strDrink > a.strDrink ? -1 : 0)
),
data.multipleCocktailsByName.map((cocktail, i) => (
<button
className="btn btn-outline-secondary p-1 menuButton btnAnimated mr-1 mb-1 border border-secondary"
key={i}
value={cocktail.strDrink}
onClick={lookupDrink}
>
<img src={cocktail.strDrinkThumb} className="menuPicture" alt="cocktail" />
{cocktail.strDrink}
</button>
)))
}
</div>
</div>
) : dataByIngredient.multipleCocktailsByIngredient !== null ? (
<div className="container">
<div className="row">
<div className="col">
<h6 className="mt-3 mb-3 text-center">Search results: </h6>
</div>
</div>
<div className="row justify-content-center">
{dataByIngredient.multipleCocktailsByIngredient.map((cocktail, i) => (
<button
className="btn btn-outline-secondary p-1 menuButton btnAnimated mr-1 mb-1 border border-secondary"
key={i}
value={cocktail.strDrink}
onClick={lookupDrink}
>
<img src={cocktail.strDrinkThumb} className="menuPicture" alt="cocktail" />
{cocktail.strDrink}
</button>
))}
</div>
</div>
) : (
<div className="row justify-content-center">
<p>No matching result</p>
</div>
))}
</div>
);
}

I am trying to refactor This code to a functional component

I am trying to refactor this code to a functional component so I dont need to wrap this in a Product Consumer. I keep getting error unexpected token in my functional component and can't figure out why.
This is the original
<ProductConsumer>
{value => {
return value.clothing.map(product => {
return <Product key={product.id} product={product} />;
});
}}
</ProductConsumer>
This is the functional Component
const ProductListComponent = (props) => {
const [loading, setloading] = useState(true)
const productConsumer = useContext(ProductContext);
const { cart } = productConsumer;
useEffect(() => {
(async () => {
await domLoaded;
setTimeout(() => {
console.log('DOM is loaded');
setLoading(false);
console.log(loading)
}, 200);
})();
}, [domLoaded, loading, params, props.location.pathname]);
if (loading === false) {
return (
<React.Fragment>
<Slide>
<header className="bg py-5 mb0 container-fluid clothing ">
<div className="container h-100">
<div className="row h-100 align-items-center">
<div className="col-lg-12">
<h1 className="display-4 text-white mt-5 mb-2 text-center">
{props.title}
</h1>
<p
style={props.textStyle}
className="lead mb-5 text-white text-center"
>
{props.description}
</p>
</div>
</div>
</div>
</header>
<div className="py-0 ">
<div className="container">
<div className="row">
return props.items.productConsumer.map(product => {
return <Product key={product.id} product={product} />;
});
</div>
</div>
</div>
</Slide>
</React.Fragment>
)
}else if (loading === true) {
return <Spinner />;
}
}
export default withRouter(ProductListComponent)
I decided to pass in the value.clothing as props so i can reuse the component
The body of a functional component is just a Function, you don't need the wrapping curly brackets:
function Component(props) {
return props.items.productConsumer.map(product => {
return <Product key={product.id} product={product} />;
});
}
This is the correct answer.
{props.items.productConsumer.map(product => {
return <Product key={product.id} product={product} />;
})}

Resources