React Hooks - UseState - Passing object from Child to Parent - reactjs

I am trying to pass data between a child and a parent component using Hooks and Functional Components.
In my parent component I have the following:-
import React, {useState} from "react"
import Card from './Card';
const CardsBoard = () => {
const { ratingObj, setRatingObj } = useState({});
console.log(ratingObj);
const cardInfo1 = {
.......
}
const cardInfo2 = {
.......
}
return (
<div className="container-fluid justify-content-center">
<div className="row">
<div className="col-md-6">
<div>
<h4>Player 1</h4>
</div>
<Card cardInfo={cardInfo1} onClick={() => setRatingObj(ratingObj)} />
</div>
<div className="col-md-6">
<h4>Player 2</h4>
<Card cardInfo={cardInfo2} onClick={() => setRatingObj(ratingObj)}/>
</div>
</div>
<div className="row top-buffer">
<div className="col-md-12 text-center">
<button id="startGameBtn" name="StartGameButton" className="btn btn-secondary">START GAME</button>
</div>
</div>
</div>
)
}
export default CardsBoard
Then in my child component I have the following:-
import React from "react"
const Card = ({ cardInfo, onClick }) => {
return (
<div className="card text-center shadow">
<h4 className="card-title">{cardInfo.title}</h4>
<div className="overflow">
<img src={cardInfo.image} alt={cardInfo.image} className="card-image" />
</div>
<div className="card-body text-dark">
<div className="container-fluid justify-content-center card-rating-text">
{
cardInfo.ratings.map(row => (
<div className="row" key={row[0].title}>
<div className="col-md-4 text-left">{row[0].title}</div>
<div className="col-md-2 text-left card-rating-color" onClick={() => onClick(cardInfo.player, row[0].title, row[0].rating)} >{row[0].rating}</div>
<div className="col-md-4 text-left">{row[1].title}</div>
<div className="col-md-2 text-left card-rating-color" onClick={() => onClick(cardInfo.player, row[1].title, row[1].rating)}>{row[1].rating}</div>
</div>
))
}
</div>
</div>
</div>
)
}
export default Card
At the moment I am getting an error:-
Uncaught TypeError: setRatingObj is not a function
at onClick (CardsBoard.js:58)
at onClick (Card.js:30)
How can I pass the onClick object from the child to the parent?
Thanks for your help and time

Change
const { ratingObj, setRatingObj } = useState({});
to
const [ ratingObj, setRatingObj ] = useState({});
useState returns a two-element array in the shape of [state, setStateFunc], so you have to apply array destructuring to it.

Related

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

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})
}

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;

showing the applicants of a job in reactjs using id

Iam trying to make a react application whereby users are applying for a job and also creating a report that will show the profile of the users who have applied for the job. Iam welcome to any ideas that can be given to me since I would like to have a logic on how to implement that.
Here is my code below:
DriverCard.js
import React from "react";
import member2 from "../../../dist/images/user2.png";
const DriverCard = ({cardValues}) => {
return (
<>
<div className="col-lg-3 col-md-3 col-sm-4 col-6 p-0 mb-5">
<div className="text-center">
<div className="mb-2">
<img
src={cardValues ? `${cardValues.location.title}` : member2}
className="rounded-circle"
height="85px"
width="85px"
alt="members"
/>
</div>
<h6 className="mb-0 textHeading">
{cardValues ? cardValues.name : `John Doe`}
</h6>
<span className="text-muted textStyle"> #JohntheD 5h</span>
<hr
style={{
width: "50%",
marginTop: 4,
marginBottom: 1,
}}
/>
<h6 className="textStyle mt-1">
{cardValues ? `${cardValues.licenseAge} +years licence` : `NA`}
</h6>
<h6 className="textStyle">
{cardValues ? `${cardValues.experience} +years experience` : `NA`}
</h6>
</div>
</div>
</>
);
};
export default DriverCard;
This is the report file that is suppossed to show the jobcard and the profile for the applicants.
import React, { useEffect, useState } from "react";
import {useHistory} from "react-router-dom";
import map from "lodash/map"
import { getSpecificJobCard } from "../../Api/index";
import arrowBack from "../../dist/images/arrowBack.svg";
import DriverCard from "./Job_Components/DriverCard";
import JobCardDetails from "./Job_Components/JobCardDetails";
import NextPageButtons from "../Main/DashBoard_Buttons/NextPageButtons";
const JobDetails = ({jobpostId}) => {
const [jobCard, setjobCard] = useState([]);
const history = useHistory();
console.log("param val",jobpostId);
useEffect(() => {
(async () => {
let jobCardRespData = await getSpecificJobCard(jobpostId);
setjobCard(jobCardRespData);
console.log("jobcard response:", jobCardRespData);
})();
}, []);
console.log("resp list ----- ", jobCard);
return (
<div className="">
<div className="col-md-10 m-auto">
<span style={{ cursor: "pointer" }} className="">
<img src={arrowBack} alt="" className="img-fluid" />
</span>
<div className="row">
<div className="col-lg-4 mt-5">
<div className="row">
{}
<JobCardDetails job={jobCard} />
</div>
</div>
<div className="col-lg-8 mt-5">
<div className="row">
<DriverCard/>
{/* <DriverCard /> */}
</div>
</div>
</div>
<div className="row">
<div className="col-lg-12">
<div className="row">
</div>
</div>
</div>
</div>
<div className="col-lg-12 my-3 border-top">
<NextPageButtons/>
</div>
</div>
);
};
export default JobDetails;

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>
);
}

How to render popup for specific element in react?

I have a list of items and for every item (.item-wrapper) i want to get a video popup with unique videoId.
I have prepared videoPopup component for that but for every item I get the the last 'videoId' of 'elements' array (the same videoId for every item).
On the other hand when I am not using PopupVideo component and just loop through items iframes I get the proper id for specific item - that is just for test purpose.
(The commented out line)
I am super new to React so I am aware that problem may be also super easy to solve.
Thanks!
Code for displaying items:
class Training extends Component {
constructor(props) {
super(props);
this.state = {
popupShowed: false
};
}
togglePopup = event => {
this.setState({
popupShowed: !this.state.popupShowed
});
};
onClosePopup = () => {
this.togglePopup();
};
content = () => {
const elements = ["76979871", "72675442", "337398380"];
const items = [];
for (const [index, value] of elements.entries()) {
items.push(
<div className="item-wrapper d-flex mb-4" key={index}>
<div className="item-col training-num text-white font-weight-normal d-flex align-items-center justify-content-center">
<span>{index < 10 ? "0" + index : index}</span>
</div>
<div className="item-col desc-col">
<h3 className="text-white title font-weight-normal">
Dlaczego warto?
</h3>
<div className="text-wrapper training-desc text-white">
<p>
Dowiesz się dlaczego Social Media Ninja to Zawód Przyszłości.
Dostaniesz wiedzę na temat oferowania i briefowania klientów i
dowiesz się jak zarabiać na social mediach.
</p>
</div>
</div>
<div className="item-col text-white time-col">
<div className="inside-wrapper">
<p className="text-nowrap">
<strong>Czas trwania:</strong> 2:25:00
<br />
<strong>Twój postęp:</strong> 90%
</p>
</div>
</div>
<div className="item-col play-col d-flex align-items-center justify-content-center d-flex align-items-center justify-content-center">
<div className="play-wrapper" onClick={this.togglePopup}>
<svg
enableBackground="new 0 0 60.001 60.001"
viewBox="0 0 60.001 60.001"
xmlns="http://www.w3.org/2000/svg"
className="play-button"
>
<path d="m59.895 58.531-29-58c-.34-.678-1.449-.678-1.789 0l-29 58c-.155.31-.139.678.044.973.182.294.504.474.85.474h58c.347 0 .668-.18.851-.474.182-.295.199-.663.044-.973zm-57.277-.553 27.382-54.764 27.382 54.764z" />
</svg>
<span className="text-white mt-2 d-inline-block">zobacz</span>
{/* <iframe src={'https://player.vimeo.com/video/' + value} width="500" height="600" frameBorder="0" allowFullScreen mozallowfullscreen="true" allowFullScreen></iframe> */}
</div>
</div>
{this.state.popupShowed ? (
<PopupVideo videoId={value} closePopup={this.onClosePopup} />
) : null}
</div>
);
}
return <div className="list-wrapper">{items}</div>;
};
render() {
return <Layout content={this.content()} />;
}
}
export default Training;
Code for displaying popupVideo:
class PopupVideo extends Component {
componentDidMount = () => {
var iframe = document.querySelector("iframe");
var player = new Player(iframe);
player.on("play", function() {
console.log("played the video!");
});
};
render() {
return (
<div className="popup video-popup">
<div className="popup-inner d-flex align-items-center d-flex justify-content-center">
<div className="video">
<span
onClick={this.props.closePopup}
className="close-video d-flex align-items-center justify-content-center"
>
<img
src=""
alt="close video"
/>
</span>
<div className="embed-container">
<iframe
src={
"https://player.vimeo.com/video/" +
this.props.videoId +
"?api=1&autoplay=0#t=0"
}
title="Nazwa szkolenia"
frameBorder="0"
allowFullScreen
mozallowfullscreen="true"
allowFullScreen
></iframe>
</div>
<div className="video-nav">
<div className="video-progress"></div>
<div className="d-flex align-items-center py-4">
<div className="play">
<span className="play-video"></span>
</div>
<div className="stop">
<span className="stop-video"></span>
</div>
<div className="volume">
<span className="volume-video"></span>
</div>
<div className="time">00:00 / 05:50</div>
<div className="break"></div>
<div className="title">
<h4>
<strong className="mr-3 pr-3">01</strong>Dlaczego warto ?
</h4>
</div>
<div className="button">
<button className="btn btn-secondary d-flex justify-content-center text-uppercase text-light font-weight-bold px-4">
Zobacz następny
</button>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default PopupVideo;
I do not have any error messages in the console.
Okay so i simplified the components to show you a good pattern to handle this kind of problems.
First create a VideoContainer to hold all the videoIds in the state.
We are going to return an array of Video components and pass the videoId as props to each one.
This container will be responsible to just provide the data to the other components
class VideoContainer extends Component {
state = {
videoIds: ["76979871", "72675442", "337398380"]
};
renderVideo = videoId => <Video key={videoId} videoId={videoId} />
render() {
const { videoIds } = this.state;
return videoIds.map(this.renderVideo);
}
}
Then create a Video component which will hold the popupShowed in state and will include the PopupVideo component only when popupShowed is true using && pass in the videoId and the togglePopup handler as props.
Now every Video is an independent component which hold the state for showing the PopupVideo
class Video extends Component {
state = {
popupShowed: false
};
togglePopup = () => {
this.setState(prevState => ({
popupShowed: !prevState.popupShowed
}));
};
render() {
const { popupShowed } = this.state;
const { videoId } = this.props;
return (
<div className="item-wrapper d-flex mb-4">
<button onClick={this.togglePopup}>Show Popup</button>
{popupShowed && (
<PopupVideo videoId={videoId} closePopup={this.togglePopup} />
)}
</div>
);
}
}
And last PopupVideo
const PopupVideo = ({ videoId, closePopup }) => (
<div className="popup video-popup">
<span onClick={closePopup}>
<img
src="https://rahimblak.com/images/video-close.png"
alt="close video"
/>
</span>
<div className="embed-container">
<iframe
src={
"https://player.vimeo.com/video/" +
videoId +
"?api=1&autoplay=0#t=0"
}
title="Nazwa szkolenia"
frameBorder="0"
allowFullScreen
mozallowfullscreen="true"
/>
</div>
</div>
);
sandbox

Resources