I have created a restaurant menu with the help of an API. Now I want to display only the data of a particular category using the category name with the help of react hooks.
Can someone guide me about the function I need to implement in my code to achieve the functionality based on my requirements.
This is App.js:
import React, { useState, useEffect } from 'react';
const Menu = () => {
/*Toggle sidemenu start*/
const [toggleState, setToggleState] = useState(1);
const toggleTab = (index) =>{
setToggleState(index);
}
/*Toggle sidemenu end*/
/* API Data start */
const [data, setData] = useState();
useEffect(() => {
async function fetchData() {
try {
const response = await fetch('https://63b040676a74151a1bbcf341.mockapi.io/restaurantmenu')
const data = await response.json();
setData(data);
console.log("data")
} catch (error) {
console.error(error);
}
}
fetchData();
}, []);
/* API Data end */
return (
<div className='main-content'>
<div className='restaurant-card'>
<div className='image'>
<img src='/images/kfc.png'/>
</div>
<div className='restaurant-details'>
<h2>KFC</h2>
<p>Abids, Hyderabad</p>
<p><img src='/images/rating.png'/><span>4.5 Rating | 09:00 A.M - 11:00 P.M</span></p>
</div>
</div>
<div className='item-category'>
<h3>Categories</h3>
<ul>
<li className={toggleState === 1 ? "tabs active" : "tabs"} onClick={()=>toggleTab(1)}>Pizza</li>
<li className={toggleState === 2 ? "tabs active" : "tabs"} onClick={()=>toggleTab(2)}>Bread</li>
<li className={toggleState === 3 ? "tabs active" : "tabs"} onClick={()=>toggleTab(3)}>Shakes</li>
<li className={toggleState === 4 ? "tabs active" : "tabs"} onClick={()=>toggleTab(4)}>Ice Cream</li>
<li className={toggleState === 5 ? "tabs active" : "tabs"} onClick={()=>toggleTab(5)}>Cakes</li>
<li className={toggleState === 6 ? "tabs active" : "tabs"} onClick={()=>toggleTab(6)}>Juices</li>
</ul>
</div>
<div className='item-list'>
<div className="box">
<input type="text" className="search-input" placeholder="Search for dishes"/>
<img src='/images/search.png'/>
</div>
<div className="content">
{
data
?
(
<div>
<div>
{data.map(item =>
<div>
<h2>{item.Category}</h2>
<div className='items' key={item.id}>
<ul>
<li>{item.Type}</li>
<li>{item.Name}</li>
<li>₹ {item.Price}</li>
<div className='hr'></div>
</ul>
</div>
</div>
)}
</div>
</div>
)
:
(<div>Loading...</div>)
}
</div>
</div>
</div>
)
}
export default Menu;
This is the sandbox link for your reference.
https://codesandbox.io/s/fragrant-wave-9b528h?file=/src/App.js
Share me with whatever the idea you have that can work for me.
You have multiples options:
Only fetch the required category with your API (you need to have the hand on it) by passing the required category to your API, like with querystrings such as fetch(https://63b040676a74151a1bbcf341.mockapi.io/restaurantmenu?category=${categoryName}), and in your API if category's querystring is defined, only show required menu related to the required category.
Filter your returned results to only display wanted categories. It's not quite recommended because you will need to wait to fetch ALL your menu, and filter them afterward so it's quite a loss of time. but it can be achieved easily by replacing your:
<div className="content">
{
data
?
by:
<div className="content">
{data?.filter(({ Category }) => Category === "WantedCategoryName")
?
Related
I made a shopping cart, when I click on the button 'add to cart' of an item it moves to local storage and I get those items from local storage and display it. But when I press the button it moves to cart but doesn't show unless I refresh the page. So how do I solve this issue. someone please help me...
My add to cart onclick function....
add_to_cart = (product) => {
let cart_data = [];
const booth_id = product.booth_id;
if (localStorage.getItem("cart") !== null) {
cart_data = JSON.parse(localStorage.getItem("cart"));
if (
cart_data[booth_id] !== null &&
cart_data[booth_id] !== undefined
) {
console.log(cart_data[booth_id]);
let already_added_products = cart_data[booth_id];
product = { ...product, quantity: 1 };
cart_data[booth_id] = [...already_added_products, product];
} else {
let booth_list = [];
product = { ...product, quantity: 1 };
cart_data[booth_id] = [...booth_list, product];
}
} else {
let booth_list = [];
product = { ...product, quantity: 1 };
cart_data[booth_id] = [...booth_list, product];
}
localStorage.setItem("cart", JSON.stringify(cart_data));
};
This is how I render it....
<div className="product-container">
{typeof this.state.products !=
"undefined" &&
this.state.products.length > 0
? this.state.products.map(
(product) => {
return (
<div
className="product-box"
key={
product.id
}
>
<div className="bg-control">
<div className="img-cover-wrap">
<img
src={
product.image
}
alt={
product.name
}
/>
<div className="hover-option">
<a
href="#!"
onClick={() =>
this.add_to_cart(
product
)
}
>
<i className="ri-shopping-cart-fill"></i>
Add
</a>
<a
href="#!"
onClick={() =>
this.product_view(
product
)
}
>
<i className="ri-eye-fill"></i>
View
</a>
</div>
</div>
<div className="product-box-body">
<h5 className="text-truncate">
{
product.name
}
</h5>
<p>
£{" "}
{
product.price
}
</p>
</div>
</div>
</div>
);
}
)
: ""}
</div>
This is how I display it in my cart (functions)...
componentDidMount() {
this.cart_data();
}
show_cart = (val) => {
this.setState({ booth_products: this.state.cart_data[val] });
};
cart_data = () => {
let temp_cart = JSON.parse(
localStorage.getItem("cart"),
(key, value) => {
if (value !== null) {
return value;
}
}
);
this.setState({ cart_data: temp_cart });
};
booth_num = (value) => {
this.setState({ booth_id: value });
this.show_cart(value);
};
My render for cart display...
<div className="product-cart-list">
<ul
className="nav nav-pills mb-3"
id="pills-tab"
role="tablist"
>
{this.state.cart_data
.length > 0
? this.state.cart_data.map(
(
booth,
index
) => {
return (
<li
className="nav-item"
role="presentation"
key={
index
}
>
<a
className="nav-link active"
id="booth-one-tab"
data-toggle="pill"
href="#booth-one"
role="tab"
aria-controls="booth-one"
aria-selected="true"
onClick={() =>
this.booth_num(
index
)
}
>
{"Booth " +
index}
</a>
</li>
);
}
)
: ""}
</ul>
</div>
<div
className="tab-content"
id="pills-tabContent"
>
<div
className="tab-pane fade show active"
id="booth-one"
role="tabpanel"
aria-labelledby="booth-one-tab"
>
<div className="scrollbar-inner-wrap">
<div className="scrollbar-wrap">
{this.state
.booth_products
.length > 0
? this.state.booth_products.map(
(
product,
index
) => (
<div
className="product-cart-wrap"
key={
index
}
>
<div className="img-cover-wrap">
<img
src={
product.image
}
alt={
product.name
}
/>
</div>
<div className="product-cart-body-wrap">
<h5 className="text-truncate">
{
product.name
}
</h5>
<div className="d-center mt-3">
<span className="price-wrap mr-3">
{"$" +
product.price}
</span>
<div className="product-count">
<form
action="#"
className="display-flex"
>
<div
className="qtyminus"
onClick={() =>
this.decrease_quantity(
product,
index
)
}
>
-
</div>
<input
type="text"
name="quantity"
value="1"
className="qty"
value={
product.quantity ||
""
}
onChange={() =>
this
.handleChange
}
/>
<div
className="qtyplus"
onClick={() =>
this.increase_quantity(
product,
index
)
}
>
+
</div>
</form>
</div>
<span className="qun-wrap mr-3">
Quantity
</span>
<button
type="button"
className="btn btn-cart-remove"
>
<i className="ri-close-circle-fill"></i>
<span
onClick={() =>
this.remove_item(
product,
index
)
}
>
Remove
</span>
</button>
</div>
</div>
</div>
)
)
: ""}
</div>
You need to listen to local storage globally. If you set up a listener to detect when local storage is updated, you can call a function to retrieve the items from local storage and set them in cart state immediately. Then, in the cart component, set it to update when the cart state is updated. You can do this with the built-in Context hook, there's no need for redux.
Create a piece of context for the cart state e.g.
import { createContext } from 'react';
//set initial value of cart to null (pre-adding anything in cart)
export const CartContext = createContext(null);
At the top level of your app, e.g. in the App.js file, add the logic to update the cart state. I used some of your code to show the rough idea of what functions should do, of course do whatever manipulations you need and probably put this in a custom hook, not actually leave in App.js:
//create cart state
const [cart, setCart] = useState(null)
// call this when local storage updates
const handleLocalStorage = () => {
if (localStorage.getItem("cart") !== null) {
// get the items from local storage
cart_data = JSON.parse(localStorage.getItem("cart"));
// etc. etc., manipulate however you want
//set items to cart state
setCart(cart_data);
}
return;
}
// listen to local storage in the browser on each render
useEffect(() => {
document.addEventListener("storage", handleLocalStorage, true);
return () => {
document.removeEventListener("storage", handleLocalStorage, true);
};
});
// pass the cart state into the context wrapper for the app
return (
<CartContext.Provider value={cart}>
// app goes here
</CartContext.Provider>
)
Finally, in your Cart component you'll have access to the cart state and can pass it the cart value to update in real-time whenever it changes.
const { cart } = useContext(CartContext)
If you're not familiar with the useContext hook: https://reactjs.org/docs/hooks-reference.html#usecontext
Essential for things like this.
Local storage won't update unless you refresh the page or add a listener (window.addEventListener("storage")...) inside another component. I think you have to rethink the structure or choose a global state approach.
Context API
React Redux
Before this, I'm using Snapshot and try to map it. But it failed for a single document. After I read back the documentations from firebase. They recommend using this way for a single document. But I don't know how to make it readable on the site rather than console. I want to send data on the title and essay.
const { id } = useParams()
useEffect(() => {
db.collection("posts").doc(id).get().then(doc => {
const newData = doc.data();
console.log(newData);
});
}, []);
return (
<div className="log">
<article className="log-details">
<div className="author-pic clearfix">
<img src={profile} alt="" />
</div>
<div className="log-preview">
<div class="cover"></div>
<h2 className="title clearfix">title here</h2>
<div className="details ">
<img src={One} alt="" />
<p className="essay">essay here</p>
</div>
</div>
</article>
</div>
)
To display the post Data use useState and set the postData with setPostData(newData). Then you can read the postData values with {postData.title} and {postData.essay} in the return statement.
Dont forget to import useState with import React, { useState, useEffect } from "react".
const { id } = useParams()
const [postData, setPostData] = useState("");
useEffect(() => {
db.collection("posts").doc(id).get().then(doc => {
const newData = doc.data();
setPostData(newData);
console.log(newData);
});
}, []);
return (
<div className="log">
<article className="log-details">
<div className="author-pic clearfix">
<img src={profile} alt="" />
</div>
<div className="log-preview">
<div class="cover"></div>
<h2 className="title clearfix">{postData && postData.title}</h2>
<div className="details ">
<img src={One} alt="" />
<p className="essay">{postData && postData.essay}</p>
</div>
</div>
</article>
</div>
)
When I click on an item it should expend some sub items. This is working but if I have two, three or four etc. list items then when I click on one it expands ALL of the sub items for all the list items which is obviously not what I want. How can I fix this code to make it only open expand the one I actually clicked on?
const [sideActive, setSideActive] = useState(false);
const toggleSideActive = () => {
setSideActive(!sideActive);
};
html:
<li>
<div
onClick={toggleSideActive}
className={
sideActive
? `${styles.navList__subheading} row ${styles.row__align_v_center} ${styles.navList__subheading__open}`
: `${styles.navList__subheading} row ${styles.row__align_v_center}`
}
>
<span className={styles.navList__subheading_icon}>
<FaBriefcaseMedical />
</span>
<span className={styles.navList__subheading_title}>
insurance
</span>
</div>
<ul
className={
sideActive
? `${styles.subList}`
: `${styles.subList} ${styles.subList__hidden}`
}
>
<li className={styles.subList__item}>medical</li>
<li className={styles.subList__item}>medical</li>
<li className={styles.subList__item}>medical</li>
</ul>
</li>
You can create a local state for tracking the selected id and show the content based on the state. Also, update the selected Id on click of the tab like below.
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [selected, setSelected] = useState("");
const data = [
{
id: 1001,
name: "Tab - 1",
content: ["test1", "test2"]
},
{
id: 1002,
name: "Tab - 2",
content: ["test21", "test22"]
}
];
return (
<div className="App">
<ul class="parent">
{data.map((v) => (
<li onClick={() => setSelected(selected !== v.id ? v.id : "")}>
{v.name}
{selected === v.id && (
<ul class="content">
{v.content.map((val) => (
<li>{val}</li>
))}
</ul>
)}
</li>
))}
</ul>
</div>
);
}
For the below example, click on the tab to see the content.
Working code - https://codesandbox.io/s/long-leaf-cl4cy?file=/src/App.js:0-759
Let me know if you are facing any issues.
When I click on a new li tag, I want the classname to change to active (which works), but also remove active from the rest of the li tags (which doesn't work). document.getElementByTagName('li').classList.remove="active", doesn't work because it is saying it is not defined. Should I go about this a different way... maybe storing something different in the state?
import React, {useState, useEffect} from 'react';
import './Anime.css';
function Anime(){
const [currentCase, setCurrentCase] = useState(0)
function getAnime(){
fetch('https://kitsu.io/api/edge/anime')
.then(response => response.json())
.then(data => console.log(data));
}
function currentSelector(e){
document.getElementsByTagName('li').clasList.remove("active");
setCurrentCase(e.target.value)
e.target.className = "active"
}
useEffect(() => {
getAnime();
}, []);
return(
<div className="anime">
{/* Selectors */}
<ul>
<li value= {0} className="active" onClick={currentSelector}>Trending</li>
<li value={1} onClick={currentSelector}>Action</li>
<li value={2} onClick={currentSelector}>Adventure</li>
<li value={3} onClick={currentSelector}>Comedy</li>
<li value={4} onClick={currentSelector}>Drama</li>
<li value={5} onClick={currentSelector}>Magic</li>
<li value={6} onClick={currentSelector}>Romance</li>
</ul>
</div>
)
}
export default Anime
Don't use the usual DOM API for things like this in React, instead use React's state management and conditional rendering functionality. You already have a state variable to track the active case (currentCase), so you can just set the class name conditionally while rendering.
For each li, just check if the value of currentCase matches the value for that li and if so, give that li the class active, otherwise give a different class.
For example:
import React, {useState, useEffect} from 'react';
import './Anime.css';
function Anime(){
const [currentCase, setCurrentCase] = useState(0)
function getAnime(){
fetch('https://kitsu.io/api/edge/anime')
.then(response => response.json())
.then(data => console.log(data));
}
function currentSelector(e){
setCurrentCase(Number(e.target.value));
}
useEffect(() => {
getAnime();
}, []);
return(
<div className="anime">
{/* Selectors */}
<ul>
<li value={0} className={currentCase === 0 ? "active" : ""} onClick={currentSelector}>
Trending
</li>
<li value={1} className={currentCase === 1 ? "active" : ""} onClick={currentSelector}>
Action
</li>
<li value={2} className={currentCase === 2 ? "active" : ""} onClick={currentSelector}>
Adventure
</li>
<li value={3} className={currentCase === 3 ? "active" : ""} onClick={currentSelector}>
Comedy
</li>
<li value={4} className={currentCase === 4 ? "active" : ""} onClick={currentSelector}>
Drama
</li>
<li value={5} className={currentCase === 5 ? "active" : ""} onClick={currentSelector}>
Magic
</li>
<li value={6} className={currentCase === 6 ? "active" : ""} onClick={currentSelector}>
Romance
</li>
</ul>
</div>
)
}
export default Anime
Or extract the class name logic into a function (defined within your Anime component) and call that function for each li element:
function getLiClassName(value) {
if (value === currentCase) {
return "active";
}
return "";
}
And use like this:
<li value={0} className={getLiClassName(0)} onClick={currentSelector}>
Trending
</li>
Here is my code I'm looking to get followers data from Firestore and then fetching users from doc id but I'm getting data in the console log but data not showing on the first time component render but when I click the tab for the second time it renders data and correct followers users showing can someone tell me what I am doing wrong ?
export default function RequestTab() {
const [followers, setfollowers] = useState(null)
useEffect(() => {
if (firebase.auth().currentUser) {
let data = [];
db.collection("buddiez")
.where("followeeId", "==", firebase.auth().currentUser.uid)
.where("accepted", "==", false)
.onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {
db.collection("users").doc(doc.data().followerId).get().then((result) => {
data.push({ ...result.data() })
});
});
});
setfollowers(data)
}
}, [])
console.log(followers);
let userArr = followers ? followers.map((follower, i) => {
return <div key={i} className="request-details pb-3 border-bottom">
<div className="noty-usepf-icon">
<img className="rounded-circle mt-0" style={{ width: "45px", height: "45px" }} src={follower.profilePic ? follower.profilePic : ""} alt="" />
</div>
<div className="request-info">
<h3 className="mb-n1" style={{ fontSize: '1.1rem' }}>{follower.fullName}</h3>
<span>#{follower.username}</span>
</div>
<div className="accept-feat">
<ul>
<li><button type="submit" className="accept-req">Accept</button></li>
<li><button type="submit" className="close-req"><i className="la la-close" /></button></li>
</ul>
</div>
</div>
}) : <h2 className="text-muted p-3 d-flex justify-content-center">No Requests !</h2>
return (
<div className="tab-pane show active">
<div className="acc-setting">
<h3>Requests</h3>
<div className="requests-list">
{userArr}
</div>
</div>
</div>
)
}
I assume Following code db.collection("users").doc(doc.data().followerId).get() is async, so setfollowers(data) will happen before these promises are resolved, so data will be empty, you have to wait until all promises are resolved and then set state, you can use Promise.All for this case.