Passing data between components into React (props) - reactjs

Into DisplayVisit component I have displayed data:
<div>
{(this.state.allVisit.length > 0) ? this.state.allVisit.map(data => {
return (
<div key={data.id} className="card-header mb-2" style={{ background: "#F0F3F7" }}>
<div className="form-group">
<label><b>VisitName:</b> {data.dataContext.VisitName} </label>
</div>
<div className="form-group">
<Link to={`/visit/${data.id}`} className="btn btn-secondary btn-sm" >
<i className="fas fa-arrow-circle-right" /> Details
</Link>
</div>
</div>
)
}) : (
<div className="card-header p-3 mb-2 text-black">
<label><b>Empty</b></label>
</div>
)
}
</div>
When user click Details is moved to next component VisitDetails - this is working.
App.js
<Route exact path="/visit/:id" component={VisitDetails}/>
Then I want have access to id and data.dataContext:
class VisitDetails extends Component {
render() {
const { id } = this.props;
const { data.dataContext } = this.props;
//or const {data} = this.prop
return (
<div>
{id}, {data.dataContext.VisitName}
</div>
)
}
}
but I'm doing something wrong. Im just learning and try to dev first app.

In your DetailVisit component :
<Link to={{ pathname: `/visit/${data.id}`, state: { data: data.dataContext.VisitName} }} className="btn btn-secondary btn-sm">
<i className="fas fa-arrow-circle-right" /> Details
</Link>
In your VisitDetails.js :
class VisitDetails extends Component {
render() {
const { id } = this.props.match.params;
const { data } = this.props.location.state;
return (
<div>
{id}, {data}
</div>
)
}
}

Related

Product Pop-up using React and Django

I am working through a tutorial on React and creating an ecommerce store with a Django backend.
I am new to React.
I have been able to get the React "FrontEnd" home page to display the products from the Django "products.py" which is a simple JSON file stored in the Django file system using the React Axios library.
There is a product pop-up which displays the product information when the use clicks on the view button.
I can't work out how to direct the pop-up to the "products.py" file.
It is still drawing its information from the products.js in the React "FrontEnd" file system.
The code is below. Any assistance would be appreciated.
The code for the product page is below.
Bestsellershop.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { handleOutofStock } from '../../../helper/shopHelper';
import { RatingShop } from '../../../helper/helper';
import Quickview from '../../layouts/Quickview';
import { Modal } from 'react-bootstrap';
import axios from 'axios'
class Bestsellershop extends Component {
constructor(props) {
super(props);
this.state = {
error:null,
modalshow: false,
lastActiveBox: -1,
data:[]
}
this.modalShow = this.modalShow.bind(this);
this.modalClose = this.modalClose.bind(this);
}
componentDidMount(){
var str = "GeeksforGeeks";
this.setState({loading:true});
axios
.get('/api/products/')
.then(res=>{
console.log(res.data);
this.setState({data:res.data, loading:false});
})
.catch(err =>{
console.log(str)
this.setState({error:err, loading:false});
});
}
// Modal
modalShow(index) {
this.setState({ modalshow: true, lastActiveBox: index });
}
modalClose() {
this.setState({ modalshow: false });
}
render() {
const{data,error,loading}=this.state;
return (
<div className="section section-padding">
<div className="container">
<div className="section-title centered">
<span className="subtitle">SuperPharmacy Compounding</span>
<h3 className="title mb-0">Our Products</h3>
</div>
<div className="row">
{error && (
<
error
header="There was some error"
content={JSON.stringify(error)}
/>
)}
{/* Data */}
{data.map((item, i) => (
<div className="col-lg-3 col-md-6" key={i}>
<div className="sigma_product style-6">
<div className="sigma_product-thumb">
<Link to={"/product-single/" + item.id}>
<img src={process.env.PUBLIC_URL + "/" + item.image[0]} alt={item.title} />
</Link>
</div>
<div className="sigma_product-body">
<h5 className="sigma_product-title"> <Link to={"/product-single/" + item.id}>{item.title}</Link>
</h5>
<div className="sigma_rating">
{RatingShop(item.rating)}
</div>
<div className="sigma_product-price">
<span>${new Intl.NumberFormat().format((item.price * (100 - item.discount) / 100).toFixed(2))}</span>
{item.discount > 0 || item.discount !== '' ?
<span>${new Intl.NumberFormat().format((item.price).toFixed(2))}</span>
: ''}
</div>
{/* Cart */}
{item.stock === true ?
<Link to="#" className="sigma_btn btn-sm">Add to Cart</Link>
:
<Link to="#" className="sigma_btn btn-sm" onClick={handleOutofStock}> Add to Cart</Link>
}
{/* Quick view */}
<Link to="#" className="sigma_btn btn-sm light" onClick={(e) => this.modalShow(item.id)}> Quick View </Link>
</div>
</div>
</div>
))}
{/* Data */}
{/* Modal (Quick View) */}
<Modal size="lg" show={this.state.modalshow} className="sigma_quick-view-modal" onHide={this.modalClose} aria-labelledby="contained-modal-title-vcenter" centered>
<Modal.Body className="sigma_modal-sec">
<div className="sigma_close" onClick={this.modalClose}>
<span />
<span />
</div>
<Quickview productId={this.state.lastActiveBox} />
</Modal.Body>
</Modal>
</div>
</div>
</div>
);
}
}
export default Bestsellershop;
The code for the modal pop-up is below.
Quickview.js
import React, { Component } from 'react';
import { getProduct, handleOutofStock } from '../../helper/shopHelper';
import { RatingShop, socialShare, getTags } from '../../helper/helper';
import { Link } from 'react-router-dom';
class Quickview extends Component {
constructor(props) {
super(props)
this.state = {
qty: 1
}
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
this.openSocialPopup = this.openSocialPopup.bind(this);
}
increment() {
this.setState({
qty: this.state.qty + 1
});
}
decrement() {
this.setState({
qty: this.state.qty > 1 ? this.state.qty - 1 : 1
});
}
handleChange(event) {
this.setState({ qty: event.target.value });
}
// Open window
openSocialPopup(social) {
window.open(social.link, "MsgWindow", "width=600,height=600")
// alert(social.title)
}
render() {
const productId = this.props.productId;
const modalContent = getProduct(productId);
return (
<div className="row sigma_product-single">
<div className="col-md-6">
<div className="sigma_product-single-thumb">
<img src={process.env.PUBLIC_URL + "/" + modalContent.image[0]} alt={modalContent.title} className="w-100" />
</div>
</div>
<div className="col-md-6">
<div className="sigma_product-single-content">
<h3>{modalContent.title}</h3>
<div className="sigma_product-price">
<span>${new Intl.NumberFormat().format((modalContent.price * (100 - modalContent.discount) / 100).toFixed(2))}</span>
{modalContent.discount > 0 || modalContent.discount !== '' ?
<span>${new Intl.NumberFormat().format((modalContent.price).toFixed(2))}</span>
: ''}
</div>
<div className="sigma_rating-wrapper">
<div className="sigma_rating">
{RatingShop(modalContent.rating)}
</div>
<span>{modalContent.reviews.length} Reviews</span>
</div>
<hr />
<p className="sigma_product-excerpt">
{modalContent.shorttext}
</p>
<div className="sigma_product-meta">
<p><strong>Product SKU: <span>#{modalContent.sku}</span></strong></p>
<p><strong>Availablity:
{modalContent.stock === true ?
<span>In Stock</span>
:
<span>Out of Stock</span>
}
</strong></p>
<p><strong>Tags: </strong>
{getTags(modalContent.tags).map((tag, i) => (
<Link to={"/shop/tag/" + tag.id} key={i}>{tag.title} ,</Link>
))}
</p>
</div>
<hr />
<form className="sigma_product-atc-form">
<div className="sigma_product-buttons d-block">
{/* Cart */}
{modalContent.stock === true ?
<button type="button" className="ml-0 btn-block sigma_btn">Add To
Cart <i className="far fa-shopping-basket" /></button>
:
<button type="button" onClick={handleOutofStock} disabled className="ml-0 btn-block sigma_btn">Add To Cart <i className="far fa-shopping-basket" /></button>
}
{/* Wishlist */}
<Link to="#" className="ml-0 btn-block sigma_btn light">Add To Wishlist <i className="far fa-heart" /> </Link>
<Link to="#" className="ml-0 btn-block sigma_btn light">Compare <i className="far fa-compress" />
</Link>
</div>
</form>
{/* Post Meta Start */}
<div className="sigma_post-single-meta">
<div className="sigma_post-single-meta-item sigma_post-share">
<h5>Share</h5>
<ul className="sigma_sm">
{/* Data */}
{socialShare(modalContent.title).map((social, i) => (
<li key={i}>
<Link to="#" onClick={(e) => this.openSocialPopup(social, i)}>
<i className={social.iconClass} />
</Link>
</li>
))}
{/* Data */}
</ul>
</div>
</div>
{/* Post Meta End */}
</div>
</div>
</div>
);
}
}
export default Quickview;
Update - I have located a helper function file called 'ShopHelper.js'. It contains a function getProduct(id). That function refers to an internal json 'shop.json'.
How do I redirect the shopBlock variable to the Django file?! I am very lost
import shopblock from '../data/shop/shop.json';
import category from '../data/shop/category.json';
// Product details
function getProduct(id) {
return shopblock.filter(product => { return product.id === parseInt(id) })[0];
}
// Count Category

Cannot redirect one page to another page in ReactJs

I'm making a function that when i click on the image container, it will open the page with the product detail with the exact detail for each specific product. However, when i click on the image, nothing happen! Please help me to find out what wrong with my codes, thank you so much!
Product.js:
class Product extends React.Component {
render() {
const { id, title, img, price, inCart } = this.props.product;
return (
<ProductWrapper clasName="col-9 mx-auto col-md-6 col-lg-3 my-3">
<div className="card">
<ProductContext.Consumer>
{(value) => (
<div className="img-container p-5">
<Router>
<Link to="/details">
<img
src={img}
alt="product"
className="card-img-top"
onClick={() => {
value.handleDetail(id);
}}
/>
</Link>
</Router>
<button
className="cart-btn"
onClick={() => value.addToCart(id)}
disabled={inCart ? true : false}
>
{inCart ? (
<p className="text-capitalize mb-0">In Cart</p>
) : (
<i class="fas fa-cart-plus"></i>
)}
</button>
</div>
)}
</ProductContext.Consumer>
<div className="card-footer d-flex justify-content-between">
<p className="align-self-center mb-0">{title}</p>
<h5 className="text-blue mb-0">
<span className="mr-1">$</span>
{price}
</h5>
</div>
</div>
</ProductWrapper>
);
}
}
context.js:
class ProductProvider extends React.Component {
state = {
products: storeProducts,
detailProduct: detailProduct
};
getItem = (id) => {
const product = this.state.products.find((item) => item.id === id);
return product;
};
handleDetail = (id) => {
const product = this.getItem(id);
this.setState(() => {
return { detailProduct: product };
});
};
addToCart = (id) => {
console.log(`hello details. id is ${id}`);
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
handleDetail: this.handleDetail,
addToCart: this.addToCart
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
Sandbox link for better observation: https://codesandbox.io/s/why-cant-i-fetch-data-from-a-passed-value-forked-30bgi?file=/src/App.js
I recommend a different approach where the product ID goes into the URL rather than being selected in context. This has a major advantage in that refreshing the details page means the product ID will be retained.
Here is a link to a working CodeSandbox.
And here are the changes I made:
In the context provider, you can remove handleDetail since the selection will instead live in the URL:
class ProductProvider extends React.Component {
state = {
products: storeProducts,
detailProduct: detailProduct
};
getItem = (id) => {
const product = this.state.products.find((item) => item.id === id);
return product;
};
addToCart = (id) => {
console.log(`hello details. id is ${id}`);
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
addToCart: this.addToCart
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
In the App component, change your details route to take an itemId parameter:
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<ProductProvider>
<Router>
<Switch>
<Route exact path="/productlist" component={ProductList} />
<Route path="/details/:itemId" component={Details} />
<Route path="*" component={() => "404 Not Found"} />
</Switch>
</Router>
</ProductProvider>
</div>
);
}
In your product component, Make the Link point to the details/itemId URL and remove any need to set that ID in context:
class Product extends React.Component {
render() {
const { id, title, img, price, inCart } = this.props.product;
return (
<ProductWrapper clasName="col-9 mx-auto col-md-6 col-lg-3 my-3">
<div className="card">
<ProductContext.Consumer>
{(value) => (
<div className="img-container p-5">
<Link to={`/details/${id}`}>
<img src={img} alt="product" className="card-img-top" />
</Link>
<button
className="cart-btn"
onClick={() => value.addToCart(id)}
disabled={inCart ? true : false}
>
{inCart ? (
<p className="text-capitalize mb-0">In Cart</p>
) : (
<i class="fas fa-cart-plus"></i>
)}
</button>
</div>
)}
</ProductContext.Consumer>
<div className="card-footer d-flex justify-content-between">
<p className="align-self-center mb-0">{title}</p>
<h5 className="text-blue mb-0">
<span className="mr-1">$</span>
{price}
</h5>
</div>
</div>
</ProductWrapper>
);
}
}
Finally, in the Details component, pluck the itemId off of the params and find the right item from your product list in context:
class Details extends React.Component {
render() {
const { itemId } = this.props.match.params;
return (
<ProductContext.Consumer>
{(value) => {
const selected = value.products.find(
(p) => p.id === parseInt(itemId)
);
if (!selected) {
return "Bad product ID: " + itemId;
}
const { id, company, img, info, price, title, inCart } = selected;
return (
<div className="container py-5">
{/* title */}
<div className="row">
<div className="col-10 mx-auto text-center text-slanted text-blue my-5">
<h1>{title}</h1>
</div>
</div>
{/* end of title */}
<div className="row">
<div className="col-10 mx-auto col-md-6 my-3">
<img src={img} className="img-fluid" alt="" />
</div>
{/* prdoduct info */}
<div className="col-10 mx-auto col-md-6 my-3 text-capitalize">
<h1>model : {title}</h1>
<h4 className="text-title text-uppercase text-muted mt-3 mb-2">
made by : <span className="text-uppercase">{company}</span>
</h4>
<h4 className="text-blue">
<strong>
price : <span>$</span>
{price}
</strong>
</h4>
<p className="text-capitalize font-weight-bold mt-3 mb-0">
some info about product :
</p>
<p className="text-muted lead">{info}</p>
{/* buttons */}
<div>
<Link to="/productlist">
<ButtonContainer>back to products</ButtonContainer>
</Link>
<ButtonContainer
cart
disabled={inCart ? true : false}
onClick={() => {
value.addToCart(id);
}}
>
{inCart ? "in cart" : "add to cart"}
</ButtonContainer>
</div>
</div>
</div>
</div>
);
}}
</ProductContext.Consumer>
);
}
}

The value didn't show up when I pass the value through fetch and componentDidMount

I am currently studying fetch and componentDidMount. I tried to get username and comment from this HTTP address http://localhost:3000/data/commentData.json and put them on the feed(kinda Instagram) but the username/comment didn't show up... If you don't mind, could you help me with which part do I miss or something wrong with my code? I really appreciate your help! I will leave a code below!
This is Comment.js what I expect to receive Username and Comment through fetch
import React, { Component } from 'react';
import COMMENT_LIST from './CommentData';
import './Comment.css';
export class Comment extends Component {
constructor() {
super();
this.state = {
commentList: [],
commentValue: '',
};
}
render() {
return (
<>
{/* {this.props.commentAddList.map((comm, idx) => {
return <li key={idx}>{comm}</li>; */}
{/* {COMMENT_LIST.map(comment => {
return (
<li className="commentContainer">
<span className="commentUserName">{comment.userName}</span>
<span className="commentContent">{comment.content}</span>
</li>
);
})} */}
{(this.props.commentList || []).map(comment => {
//props
return (
<li className="commentContainer">
<span className="commentUserName">{comment.userName}</span>
<span className="commentContent">{comment.content}</span>
</li>
);
})}
</>
);
}
}
export default Comment;
This is Feed.js that I tried to add a comment with username and content
import React, { Component } from 'react';
import './Feed.scss';
import {
FaRegHeart,
FaRegComment,
FaRegPaperPlane,
FaRegBookmark,
FaEllipsisH,
} from 'react-icons/fa';
import Comment from '../../../compoonents/Comment/Comment';
import selfiImg from '../../../assets/images/about.png';
import ucscPic from '../../../assets/images/ucscPic.png';
class Feed extends Component {
constructor() {
super();
this.state = {
value: '',
commentList: [],
};
}
componentDidMount() {
fetch('http://localhost:3000/data/commentData.json', {
method: 'GET',
})
.then(res => res.json())
.then(data => {
this.setState({
commentList: data,
});
});
}
getValue = event => {
this.setState({
value: event.target.value,
});
};
addComment = () => {
this.setState({
commentList: this.state.commentList.concat([this.state.value]),
value: '',
});
};
addCommEnter = e => {
if (e.key === 'Enter') {
this.addComment();
}
};
render() {
return (
<div className="feeds">
<div className="article">
<div className="identi">
<img className="selfi-identi" alt="selfi-img" src={selfiImg} />
<span className="name"> Jiwan Jeon </span>
{/* <i id="test" class="fa fa-ellipsis-h" aria-hidden="true"></i> */}
<div className="faEllipsisH">
{/* <FontAwesomeIcon icon={faEllipsisH} /> */}
<FaEllipsisH />
</div>
</div>
<div className="pic">
<img id="feed-pic" src={ucscPic} />
</div>
<div className="show-box">
<div className="reaction-icons">
<FaRegHeart className="heart" />
<FaRegComment className="comment" />
<FaRegPaperPlane className="plane" />
<FaRegBookmark className="bookMark" />
{/* <FontAwesomeIcon icon={faHeart} className="heart" />
<FontAwesomeIcon icon={faComment} className="comment" />
<FontAwesomeIcon icon={faPaperPlane} className="plane" />
<FontAwesomeIcon icon={faBookmark} className="bookMark" /> */}
</div>
<div className="like-counts">
<span>like 4,000</span>
</div>
<div className="check-comments">
<span>
UC Santa Cruz will continue to offer most courses remotely or
online for spring and summer 2021, providing in-person
instruction for a small
</span>
<a id="space" href="">
expanding
</a>
<br />
Check the all comments
<ul className="feed-comments">
<Comment commentAddList={this.state.commentList} />
{/* <li>hello</li> */}
{/* {this.state.commentList.map((comm, idx) => {
return <li key={idx}>{comm}</li>;
})} */}
</ul>
</div>
</div>
<div className="comment">
<i className="fa fa-smile-o" />
<input
// onChange={this.textChange}
// onKeyPress={this.enterClick}
onKeyPress={this.addCommEnter}
onChange={this.getValue}
className="user-input"
type="text"
placeholder="Add Comment..."
value={this.state.value}
/>
<button onClick={this.addComment} className="post">
Post
</button>
</div>
</div>
</div>
);
}
}
export default Feed;
Within your Comment.js file this line is giving you the error: this.props.commentList, because you didn't define such a prop (maybe you misspelled this.props.commentAddList..

I want to use react hook 'useState' to save the information fetched from API and show on the scrren. But, I can't use in class so what can I do?

So, for some context I'm fetching some search results on click on search button. The result shows in console.log() after clicking search button perfectly. the SearchComponent.js is below.
import React, { Component, useState } from 'react'
import { API_KEY, API_URL } from '../config/keys';
import { Link } from 'react-router-dom';
class SearchBox extends Component {
constructor(props) {
super(props);
this.state = {
searchQuery: ""
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.onChangeValue = this.onChangeValue.bind(this);
}
handleChange = (e) => {
e.preventDefault();
const { name, value } = e.target;
this.setState({ [name]: value });
}
handleSubmit = (e) => {
e.preventDefault();
var data = this.state.searchQuery;
const url = `${API_URL}search/${this.state.selectedOption}?api_key=${API_KEY}&language=en-US&query=${encodeURI(data)}&page=1&include_adult=false`;
fetch(url)
.then(response => response.json())
.then(response => {
console.log(response);
const result = response.results;
})
}
onChangeValue(event) {
this.setState({
selectedOption: event.target.value
});
}
render() {
return (
<>
<div className="mt-3">
{/* Breadcrumb */}
<div style={{ width: '95%', margin: '1rem auto' }}>
<nav aria-label="breadcrumb">
<ol className="breadcrumb">
<li className="breadcrumb-item"><Link to='/'>Home</Link></li>
<li className="breadcrumb-item active" aria-current="page">Search</li>
</ol>
</nav>
</div>
<div className="row">
<div className="col-6 offset-3">
<form className="form-group">
<div className="input-group">
<input
className="form-control"
type="text"
placeholder= 'Search...'
onChange={this.handleChange}
name= 'searchQuery'
value={this.state.searchQuery} />
<button onClick={this.handleSubmit} type="submit" className="btn btn-primary input-group-addon" ><span className="fa fa-search fa-lg"></span></button>
</div>
{/* radio buttons */}
<div className="mt-2">
<div class="form-check">
<input
class="form-check-input"
type="radio"
name="movie"
value="movie"
checked={this.state.selectedOption === "movie"}
onChange={this.onChangeValue}
/>
<span class="form-check-label font-weight-bold">
Movies
</span>
</div>
<div class="form-check">
<input
class="form-check-input"
type="radio"
value="tv"
name="tvshow"
checked={this.state.selectedOption === "tv"}
onChange={this.onChangeValue}
/>
<span class="form-check-label font-weight-bold">
TV Shows
</span>
</div>
</div>
</form>
</div>
</div>
</div>
{/* search results */}
<div style={{ width: '95%', margin: '1rem auto' }}>
<div className="text-center">
<div className="font-weight-lighter h2"> Search Results </div>
</div>
</div>
</>
)
}
}
export default SearchBox;
the array of result is in result variable of handleSubmit(). how can I use useState to store my result var and then show it on scrren.
if you don't understand how i'm talking about using this hook i've attached a file which does similar thing.
landingComponent.js
import React, { useEffect, useState } from 'react';
import {API_URL, API_KEY, IMAGE_URL} from '../../config/keys';
import { Row } from 'reactstrap';
import MainImage from './MainImage';
import GridCard from './GridCard';
import { Link } from 'react-router-dom';
function LandingPage() {
const [Movies, setMovies] = useState([]);
const [CurrentPage, setCurrentPage] = useState(0);
useEffect( () => {
const endpoint = `${API_URL}movie/popular?api_key=${API_KEY}&language=en-US&page=1`;
fetchMovies(endpoint);
}, []);
const fetchMovies = (path) => {
fetch(path)
.then(response => response.json())
.then(response => {
console.log(response);
setMovies([...Movies, ...response.results]);
setCurrentPage(response.page);
})
}
const handleClick = () => {
const endpoint = `${API_URL}movie/popular?api_key=${API_KEY}&language=en-US&page=${CurrentPage + 1}`
fetchMovies(endpoint);
}
return (
<div style={{ width: '100%', margin: 0 }} >
<div style={{ width: '95%', margin: '1rem auto' }}>
{/* Breadcrumbs */}
<nav aria-label="breadcrumb">
<ol className="breadcrumb">
<li className="breadcrumb-item"><Link to='/'>Home</Link></li>
<li className="breadcrumb-item active" aria-current="page">Movies</li>
</ol>
</nav>
<div className="font-weight-bold h2"> Latest Movies </div>
<hr style={{borderColor:'black'}}/>
<Row>
{Movies && Movies.map((movie, index) => (
<React.Fragment key={index}>
<GridCard
image={movie.poster_path && `${IMAGE_URL}w500${movie.poster_path}`}
movieId={movie.id} movieTitle={movie.title} name={movie.original_title}
/>
</React.Fragment>
))}
</Row>
<br />
<div className="text-center">
<button className="btn btn-primary" onClick={handleClick}> Load More </button>
</div>
</div>
</div>
)
}
export default LandingPage
I want to use same way in SearchComponent.js. I tried so many thing but none worked. Help is appreciated.
React has two ways of using components :
Class components
Declared this way : class ComponentName extends Component {
then your state is managed using : this.setState()
Function components
Declared just as a function as your second example
There you can use hooks and the useState()
So if you're not planning to re-write all your component you'll have to use this.setState()
When you fetch the data you need to store it in the state again creating results:[] property in the state.
handleSubmit = (e) => {
e.preventDefault();
var data = this.state.searchQuery;
const url = `${API_URL}search/${this.state.selectedOption}?api_key=${API_KEY}&language=en-US&query=${encodeURI(data)}&page=1&include_adult=false`;
fetch(url)
.then(response => response.json())
.then(response => {
console.log(response);
const result = response.results;
this.setState({results:result}); //Here you store the state.
});
}
Now you will have to show it in the results JSX block.
{/* search results */}
<div style={{ width: '95%', margin: '1rem auto' }}>
<div className="text-center">
<div className="font-weight-lighter h2"> Search Results </div>
<div className="results">
<ul>
{this.state.results.length && this.state.results.map(item => { return (
<li> {item} </li>
) })}
</ul>
</div>
</div>
</div>

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

Resources