Uncaught TypeError: Cannot read properties of undefined (reading 'longdescription_title') when page is refreshed - javascript

I'm pretty new to react, and I am trying to make an Accordion Component with multiple dropdowns. I am trying to load my data from my database. I had thought I got it to work because it showed my data in the correct areas, but when I refreshed the page I got an Uncaught TypeError: Cannot read properties of undefined (reading 'longdescription_title') error. I'm not sure why this is happening, and I would really appreciate any help or advice on how to fix this problem.
Thank you!
import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { detailsProduct } from '../actions/productActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import '../components/Accordion.css'
import { IconContext } from 'react-icons';
import { FiPlus, FiMinus } from 'react-icons/fi';
export default function ProductScreen(props) {
const dispatch = useDispatch();
const productId = props.match.params.id;
const [accordionItems, setAccordionItems] = useState([]);
const [accordionTitles, setAccordionTitles] = useState([]);
const [clicked, setClicked] = useState(false);
const productDetails = useSelector((state) => state.productDetails);
const { loading, error, product } = productDetails;
const userSignin = useSelector((state) => state.userSignin);
const { userInfo } = userSignin;
useEffect(() => {
if (product) {
const accordionItems = [product.how_to_use];
accordionItems.unshift(product.ingredients);
accordionItems.unshift(product.longdescription);
setAccordionItems(accordionItems);
}
}, [product]);
useEffect(() => {
if (product) {
const accordionTitles = [product.how_to_use_title];
accordionTitles.unshift(product.ingredients_title);
accordionTitles.unshift(product.longdescription_title);
setAccordionTitles(accordionTitles);
}
}, [product]);
const Items = [...accordionItems];
const Titles = [...accordionTitles];
const accordion = [
{title: product.longdescription_title, body: product.longdescription},
{title: product.ingredients_title, body: product.ingredients},
{title: product.how_to_use_title, body: product.how_to_use},
]
const toggle = index => {
if (clicked === index) {
return setClicked(null);
}
setClicked(index);
};
return (
<div>
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<div>
<Link to="/body">Back to result</Link>
<div className="row top">
<div className="col-1">
<ul>
<li>
<h1>{product.name}</h1>
</li>
<li>
<div>
<IconContext.Provider value={{ color: 'black', size: '2vw' }}>
<div className="accordionSection">
<div className = "container">
{accordion && accordion.length ? (
accordion.map((item, index) => {
return (
<>
<div className = "wrap" onClick={() => toggle(index)} key={index}>
<h1>{item.title}</h1>
<span>{clicked === index ? <FiMinus /> : <FiPlus />}</span>
</div>
{clicked === index ? (
<div className="dropdown">
<p>{item.body}</p>
</div>
) :
null}
</>
);
})
) : (
<></>
)}
</div>
</div>
</IconContext.Provider>
</div>
</li>
</ul>
</div>
<li>
<button onClick={addToCartHandler} className="primary block">
Add to Cart
</button>
</li>
</>
)}
</ul>
</div>
</div>
</div>
);
}

Initialize the Items, Titles and accordion variables only when the product has been set (in the useEffect calls).
You should use a separate state to store the accordion array.
Also, no need to use separate useEffect calls:
let Items = [];
let Titles = [];
const [accordion, setAccordion] = useState([]);
useEffect(() => {
if (product) {
const accordionItems = [product.how_to_use];
accordionItems.unshift(product.ingredients);
accordionItems.unshift(product.longdescription);c
setAccordionItems(accordionItems);
Items = [...accordionItems];
const accordionTitles = [product.how_to_use_title];
accordionTitles.unshift(product.ingredients_title);
accordionTitles.unshift(product.longdescription_title);
setAccordionTitles(accordionTitles);
Titles = [...accordionTitles];
setAccordion([
{ title: product.longdescription_title, body: product.longdescription },
{ title: product.ingredients_title, body: product.ingredients },
{ title: product.how_to_use_title, body: product.how_to_use },
]);
}
}, [product]);

Related

hiding and showing array of images

So I have an array of images, which I would like to hide or show on a click of a button.
right now when I try to hide the image, it will hide the entire array.
import "./main.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import React, { useEffect, useState } from "react";
import {
faCircleChevronLeft,
faCircleChevronRight,
faCircleXmark,
} from "#fortawesome/free-solid-svg-icons";
const Main = ({ galleryImages }) => {
const [slideNumber, setSlideNumber] = useState(0);
const [openModal, setOpenModal] = useState(false);
const [pics, setPics] = useState([]);
const [show, toggleShow] = useState(true);
// buttons next to name of diff charts (hide/show chart)
const handleOpenModal = (index) => {
setSlideNumber(index);
setOpenModal(true);
};
const removeImage = (id) => {
setPics((oldState) => oldState.filter((item) => item.id !== id));
};
// const hide = () => {
// setShow(false)
// }
const handleCloseModal = () => {
setOpenModal(false)
}
useEffect(()=> {
setPics(galleryImages)
},[]);
return (
<div>
<button onClick={() => toggleShow(!show)}>toggle: {show ? 'show' : 'hide'}</button>
{show &&
<div>
{pics.map((pic) => {
return (
<div style = {{marginBottom:'100px'}}>
{pic.id}
<img
src={pic.img}
width='500px'
height='500px'
/>
<button onClick ={() => removeImage(pic.id)}>Delete</button>
</div>
)
})}
</div>
I tried making a state component to try to hide and show the images, however it will hide the entire array instead of the individual image
i would add a show var to the galleryImages array and then set it so you get control of each image like this
import { useState } from "react";
import { v4 as uuidv4 } from "uuid";
import "./main.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import React, { useEffect, useState } from "react";
import {
faCircleChevronLeft,
faCircleChevronRight,
faCircleXmark,
} from "#fortawesome/free-solid-svg-icons";
function Main({ galleryImages }) {
const [slideNumber, setSlideNumber] = useState(0);
const [openModal, setOpenModal] = useState(false);
const [pics, setPics] = useState([]);
// buttons next to name of diff charts (hide/show chart)
const toggleShow = ({ id, show }) => {
setPics((oldState) =>
oldState.map((item) => {
if (item.id !== id) return item;
return { ...item, show: !show };
})
);
};
const removeImage = (id) => {
setPics((oldState) => oldState.filter((item) => item.id !== id));
};
useEffect(() => {
setPics(
galleryImages.map((galleryImage) => {
return { ...galleryImage, show: true };
})
);
}, []);
return (
<div>
<div>
{pics.map((pic) => {
return (
<>
<button
onClick={() => toggleShow({ show: pic.show, id: pic.id })}
>
toggle: {pic.show ? "show" : "hide"}
</button>
{pic.show && (
<div style={{ marginBottom: "100px" }}>
{pic.id}
<img src={pic.img} width="500px" height="500px" />
<button onClick={() => removeImage(pic.id)}>Delete</button>
</div>
)}
</>
);
})}
</div>
</div>
);
}
export default Main;
`
If you would like the option to hide individual pics, to accomplish this you are correct in your state component approach.
First you can create a pic component that has its own state with a hide/show button:
export default function Pic({pic}) {
const [showPic, setShowPic] = useState(true);
const handleClick = () => {setShowPic(!showPic)}
return (
<div>
<div style={showPic ? {display : "block"} : {display : "none"}}>
<img
src={pic.img}
width='500px'
height='500px'
/>
</div>
<button onClick={handleClick}>{showPic ? 'Hide' : 'Show'}</button>
</div>
)
}
Next, you can import this component into your main file
import Pic from 'location/Pic.js';
and map each pic to a <Pic> component.
{pics.map((pic) => <Pic pic={pic} key={pic.id}/>)}
Now the images will be shown each with their own Hide/Show button that can toggle their display with the state contained within each <Pic/> component. This is good practice because toggling one image will not cause a re-render of the entire image gallery.

React js useState&useEffect array duplicates elements after a change

I am a beginner in react js programming. I'm trying to do the todo project, which is a classic project. When I delete or add an element from the list, the newly formed list appears on the screen by combining with the previous one, I will show it with a picture below. I did not understand the source of the eror so wanted to post it here to get some advices suggestions about why it is happening.Thank you.(I am getting and storing data in firebase firestore database)
Before Adding an element initial array state
After adding an element to the array.
I am using useState for array and using useEffect to get initial data
MainPage.js that contains form and the list components.
const MainPage = () => {
const [isLoading, setLoding] = useState(true);
const [array, setArray] = useState([]);
const sub = async (email) => {
var result = [];
await onSnapshot(doc(db, "users", email), (doc) => {
var data = doc.data().todos;
data.forEach((element) => {
Object.keys(element).map(() => {
result.push(element["title"]);
});
});
setArray(result);
setLoding(false);
});
};
useEffect(() => {
sub(auth.currentUser.email);
}, []);
const onAddToDo = (todoTitle) => {
setArray((prevAray) => {
return [...prevAray, todoTitle];
});
};
const onRemove = (title) => {
setArray((prevAray) => {
return [array.pop(array.indexOf(title))];
});
};
return (
<div>
{isLoading && <h1>Loading</h1>}
{!isLoading && (
<div>
<section>
<NavBar></NavBar>
<ToDoForm passData={onAddToDo} />
</section>
<section>
<CardList removeCards={onRemove} array={array} />
</section>
</div>
)}
</div>
);
};
export default MainPage;
Firebase.js that stores the firebase update methods
export const deleteItem = (title) => {
updateDoc(doc(db, "users", auth.currentUser.email), {
todos: arrayRemove({ title: title }),
});
};
export const addnewTodo = (title) => {
updateDoc(doc(db, "users", auth.currentUser.email), {
todos: arrayUnion({ title: title }),
});
};
TodoForm.js component
const ToDoForm = (props) => {
const [todoTitle, setTitle] = useState("");
const titleChangeHandler = (event) => {
setTitle(event.target.value);
};
const newTodoAdder = (event) => {
event.preventDefault();
addnewTodo(todoTitle);
props.passData(todoTitle);
};
return (
<div className="form_holder">
<div className="form_container">
<form onSubmit={newTodoAdder}>
<h3>Add Events</h3>
<label>Title</label>
<input
onChange={titleChangeHandler}
type="text"
placeholder="Title"
id="title"
></input>
<div className="holder">
<button type="sumbit">Add</button>
</div>
</form>
</div>
</div>
);
};
export default ToDoForm;
CardList.js component
const CardList = (props) => {
const array = props.array;
if (array.length === 0) {
return (
<div className="grid_container">
<h2>Found no todos</h2>
</div>
);
}
return (
<div className="grid_container">
{array.map((element, index) => {
return (
<Card
removeSelf={() => {
props.removeCards(element);
}}
key={index}
title={element}
/>
);
})}
</div>
);
};
export default CardList;
Card.js component
const Card = (props) => {
const handleRemove = (event) => {
event.preventDefault();
deleteItem(props.title);
props.removeSelf();
};
return (
<div className="card">
<h2 className="card__title">{props.title}</h2>
<button type="button" onClick={handleRemove}>
Delete
</button>
</div>
);
};
export default Card;
EDIT ;
Index.js file
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
SOLUTION
I fixed the issue by changing the add and remove functions that were inside of MainPage.js file You can see the new versions bellow. Hope someday it will help somebody.
Use effect was called once all I had to do get the data again after a change...
New Remove and Add functions
const onAddToDo = (todoTitle) => {
console.log(todoTitle + " Added");
sub(auth.currentUser.email);
};
const onRemove = (title) => {
console.log(title + " Deleted");
sub(auth.currentUser.email);
};

ReactJs: TypeError: Cannot assign to read only property 'cartHandler' of object '#<Object>'?

Here I'm trying to build a restaurant website. I already build half part of it. But I'm stuck at some point. I'm getting error. I can't find out in my code what is wrong. Whenever I tried to add new food by clicking add button Then I get the TypeError: Cannot assign to read-only property 'cartHandler' of object '#'?.. I tried to debug in cartHandler object but I'm getting the same errors many times. Can someone check it out, please?
Here is my App.js FIle
import logo from './logo.svg';
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
import './App.css';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css'
import Banner from './components/Banner/Banner';
import Header from './components/Header/Header';
import Foods from './components/Foods/Foods';
import Features from './components/Features/Features';
import Footer from './components/Footer/Footer';
// import SIgnUp from './components/SignUp/SIgnUp';
import NotFound from './components/NotFound/NotFound';
import FoodItemDetails from './components/FoodItemDetails/FoodItemDetails'
import { createContext, useContext, useState } from 'react';
import Login from './components/Login/Login';
export const userContext = createContext();
function App() {
const [cart , setCart] = useState([]);
const [orderId , setOrderId] = useState(null);
const [deliveryDetails , setDeliveryDetails] = useState({
todoor:null,road:null, flat:null, businessname:null, address: null
});
const [userEmail, setUserEmail] = useState(null);
const deliveryDetailsHandler = (data) => {
setDeliveryDetails(data)
}
const getUserEmail = (email) => {
setUserEmail(email);
}
const clearCart = () => {
const orderedItems = cart.map(cartItem => {
return {food_id : cartItem.id, quantity: cartItem.quantity}
})
const orderDetailsData = { userEmail , orderedItems, deliveryDetails }
fetch('https://red-onion-backend.herokuapp.com/submitorder' , {
method : "POST",
headers: {
"Content-type" : "application/json"
},
body : JSON.stringify(orderDetailsData)
})
.then(res => res.json())
.then(data=> setOrderId(data._id))
console.log(orderId);
setCart([])
}
const cartHandler = (data) => {
const alreadyAdded = cart.find(crt => crt.id == data.id );
const newCart = [...cart,data]
setCart(newCart);
if(alreadyAdded){
const reamingCarts = cart.filter(crt => cart.id != data);
setCart(reamingCarts);
}else{
const newCart = [...cart,data]
setCart(newCart);
}
}
const checkOutItemHandler = (productId, productQuantity) => {
const newCart = cart.map(item => {
if(item.id == productId){
item.quantity = productQuantity;
}
return item;
})
const filteredCart = newCart.filter(item => item.quantity > 0)
setCart(filteredCart)
}
const [logggedInUser, setLoggedInUser] = useState({});
const [signOutUser, setSignOutUser] = useState({});
return (
<userContext.Provider value={([logggedInUser, setLoggedInUser], [signOutUser, setSignOutUser])}>
<Router>
<div className="App">
<Switch>
<Route exact path="/">
<Header></Header>
<Banner></Banner>
<Foods></Foods>
<Features></Features>
<Footer></Footer>
</Route>
<Route path="/user">
<Login></Login>
</Route>
<Route path= "/food/:id">
<Header cart={cart}></Header>
{/* <FoodItemDetails cart={cart} cartHandler={cartHandler}></FoodItemDetails> */}
<FoodItemDetails cart={cart} cartHandler={cartHandler}></FoodItemDetails>
<Footer></Footer>
</Route>
<Route path ="*">
<NotFound></NotFound>
</Route>
</Switch>
</div>
</Router>
</userContext.Provider>
);
}
export default App;
Here is my FoodItemDetails.js File
import React, { useEffect, useState } from "react";
import { useParams } from "react-router";
// import { useParams } from "react-router";
// import PreLoader from "../PreLoader/PreLoader";
import { FontAwesomeIcon} from '#fortawesome/react-fontawesome'
import { faCartArrowDown, faCheckCircle } from '#fortawesome/free-solid-svg-icons'
import PreLoader from "../PreLoader/PreLoader";
const FoodItemDetails = (props) => {
// const {name,shortDescription,price,images} = props.food;
console.log(props, "nashir")
const [currentFood, setCurrentFood] = useState({});
const {id} = useParams();
console.log(id);
const [quantity, setQuantity] = useState(1);
const [isSuccess, setIsSuccess] = useState(false);
const [selectedBigImage, setSelectedBigImage] = useState(null);
const [preloaderVisibility, setPreloaderVisibility] = useState("block");
// const [quantity, setQuantity] = useState(1);
useEffect(() => {
fetch("https://red-onion-backend.herokuapp.com/food/" + id)
.then((res) => res.json())
.then((data) => {
setCurrentFood(data);
setPreloaderVisibility("none");
})
.catch((err) => console.log(err));
if (currentFood.images) {
setSelectedBigImage(currentFood.images[0]);
}
window.scrollTo(0, 0);
}, [currentFood.name]);
const finalCartHandler = (currentFood) => {
currentFood.quantity = quantity;
console.log(currentFood, 'food man');
props.cartHandler = currentFood;
setIsSuccess(true);
console.log(isSuccess, "what");
}
if(isSuccess){
setTimeout(() => {
setIsSuccess(false)
console.log(isSuccess);
}, 1500);
}
return (
<div className="food-details container my-5">
<PreLoader visibility={preloaderVisibility}></PreLoader>
{currentFood.name && (
<div className="row">
<div className="col-md-6 my-5">
<h1>{currentFood.name}</h1>
<p className="my-5">{currentFood.fullDescription}</p>
<div className="d-flex my-4">
<h2>${currentFood.price.toFixed(2)}</h2>
<div className="cart-controller ml-3">
<button
className="btn"
onClick={() => setQuantity(quantity <= 1 ? 1 : quantity - 1)}
>
-
</button>
<button
className="btn"
onClick={() => setQuantity(quantity + 1)}
>
+
</button>
</div>
</div>
<div className="action d-flex align-items-center">
<button className="btn btn-danger btn-rounded" onClick={() => finalCartHandler(currentFood)}><FontAwesomeIcon icon={faCartArrowDown} /> Add</button>
</div>
<div className="more-images mt-5">
{currentFood.images.map((img, index) => (
<img
onClick={() => setSelectedBigImage(currentFood.images[index])}
className={
currentFood.images[index] === selectedBigImage
? "mr-4 small-img active-small-img"
: "mr-4 small-img"
}
height="150px"
src={img}
alt=""
/>
))}
</div>
</div>
<div className="col-md-6 my-5">
<img src={selectedBigImage} className="img-fluid" alt="" />
</div>
</div>
)}
</div>
);
};
export default FoodItemDetails;
First of all you can not change property of props. They are read-only properties.
Second Your cartHnadler is function and not property but you are assigning value instead of calling function.
props.cartHandler = currentFood; // here you are assigning value
To solve this you need to call cartHandler and pass currentFood as argument like below:-
props.cartHandler(currentFood);
Props are a read-only property. Inside finalCartHandler, you are trying to assigning props.cartHandler to currentFood. I assume you're wanting to invoke props.cartHandler instead of assigning it.
I also notice inside cartHandler that you are checking if the id is the same with == which is a truthy/falsy equals, whereas === would work better (also in a few other spots with == and !=).

Why react loads images for many times? is it possible to save the first load?

to watch the problem you can vivsit the test site http://u100525.test-handyhost.ru/products
the problem appears if to click many times on category items, images of products start to bug becouse react loads image of one item over and over again, on every change of category - on every filter of products, so how to make one load and save somehow the loaded images?
so if i click on categories my code is filtering products array and update statement - visibleProducts then im doing visibleProducts.map((product)=>{});
and i`m getting bug problem, because every time when react renders my the component does request to the server for getting image by id and waits while the image will load, but if i click on an other category react(ProductItem) starts other request for new images then it is starting to bug they start blinking and changing ;c
im new in react and just stated to practice what i have to do guys?
is my code correct ?
here is my ProductItem component ->
import React, { useState, useEffect, memo, useCallback } from "react";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import { setModalShow, onQuickViewed, addedToCart } from "../../actions";
import Checked from "../checked";
import "./product-item.css";
import Spinner from "../spinner";
const ProductItem = ({
product,
wpApi,
addedToCart,
onQuickViewed,
setModalShow,
}) => {
const [prodImg, setProdImg] = useState("");
const [animated, setAnimated] = useState(false);
const [checked, setChecked] = useState(false);
const [itemLoading, setItemLoading] = useState(true);
const checkedFn = useCallback(() => {
setChecked(true);
setTimeout(() => {
setChecked(false);
}, 800);
},[product]);
const onModalOpen = useCallback((e, id) => {
onQuickViewed(e, id);
setModalShow(true);
}, product);
const addHandle = useCallback((e, id) => {
e.preventDefault();
addedToCart(id);
checkedFn();
},[product]);
useEffect(()=>{
setItemLoading(false);
}, [prodImg]);
useEffect(() => {
wpApi.getImageUrl(product.imageId).then((res) => {
setProdImg(res);
});
});
return (
<div className="product foo">
<div
className='product__inner'}
>
{!itemLoading? <div
className="pro__thumb"
style={{
backgroundImage:prodImg
? `url(${prodImg})`
: "assets/images/product/6.png",
}}
>
<Link
to={`/product-details/${product.id}`}
style={{ display: `block`, width: `100%`, paddingBottom: `100%` }}
>
</Link>
</div>: <Spinner/>}
<div className="product__hover__info">
<ul className="product__action">
<li>
<a
onClick={(e) => {
onModalOpen(e, product.id);
}}
title="Quick View"
className="quick-view modal-view detail-link"
href="#"
>
<span ><i class="zmdi zmdi-eye"></i></span>
</a>
</li>
<li>
<a
title="Add TO Cart"
href="#"
onClick={(e) => {
addHandle(e, product.id);
}}
>
{checked ? (
<Checked />
) : (
<span className="ti-shopping-cart"></span>
)}
</a>
</li>
</ul>
</div>
</div>
<div className="product__details">
<h2>
<Link to={`/product-details/${product.id}`}>{product.title}</Link>
</h2>
<ul className="product__price">
<li className="old__price">${product.price}</li>
</ul>
</div>
</div>
);
};
const mapStateToProps = ({ options, cart, total, showModal }) => {
return {};
};
const mapDispatchToProps = {
onQuickViewed,
setModalShow,
addedToCart,
};
export default connect(mapStateToProps, mapDispatchToProps)(memo(ProductItem));
here is my parent component Products ->
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import ProductItem from "../product-item";
import { withWpApiService } from "../hoc";
import { onQuickViewed, addedToCart, categoriesLoaded } from "../../actions";
import CategoryFilter from "../category-filter";
import Spinner from "../spinner";
import "./products.css";
const Products = ({
maxProducts,
WpApiService,
categoriesLoaded,
addedToCart,
onQuickViewed,
products,
categories,
loading,
}) => {
const [activeIndex, setActiveIndex] = useState(0);
const [activeCategory, setActiveCategory] = useState(0);
const [visibleProducts, setVisibleProducts] = useState([]);
const wpApi = new WpApiService();
useEffect(() => {
updateVisibleProducts(activeCategory, products);
}, [products]);
useEffect(() => {
wpApi.getCategories().then((res) => {
categoriesLoaded(res);
});
}, []);
const getCatId = (cat) => {
setActiveCategory(cat);
updateVisibleProducts(cat, products);
setActiveIndex(cat);
};
const updateVisibleProducts = (category, products) => {
let updatedProducts = [];
switch (category) {
case 0:
updatedProducts = products;
setVisibleProducts(updatedProducts);
break;
default:
updatedProducts = products.filter(
(product) => product.categories.indexOf(category) >= 0
);
setVisibleProducts(updatedProducts);
}
};
let currentLocation = window.location.href.split("/");
if (!loading) {
return (
<section className="htc__product__area shop__page mb--60 mt--130 bg__white">
<div className={currentLocation[3] == "" ? `container` : ""}>
<div className="htc__product__container">
<CategoryFilter
activeIndex={activeIndex}
categories={categories}
getCatId={getCatId}
/>
<div
className="product__list another-product-style"
style={{ height: "auto" }}
>
{visibleProducts
.slice(0, maxProducts ? maxProducts : products.length)
.map((prod, id) => {
return (
<ProductItem
wpApi={wpApi}
key={id}
onQuickViewed={onQuickViewed}
addedToCart={addedToCart}
product={prod}
/>
);
})}
</div>
</div>
</div>
</section>
);
} else {
return <Spinner />;
}
};
const mapStateToProps = ({ products, loading, activeCategory, categories }) => {
return {
products,
activeCategory,
categories,
loading,
};
};
const mapDispatchToProps = {
addedToCart,
categoriesLoaded,
onQuickViewed,
};
export default withWpApiService()(
connect(mapStateToProps, mapDispatchToProps)(Products)
);
and if you need, here is my CategoryFilter component ->
import React from 'react'
const CategoryFilter = ({categories, getCatId, activeIndex}) => {
return (
<div className="row mb--60">
<div className="col-md-12">
<div className="filter__menu__container">
<div className="product__menu">
{categories.map((cat) => {
return (
<button key={cat.id}
className={activeIndex === cat.id? 'is-checked' : null}
onClick={() => getCatId(cat.id)}
data-filter=".cat--4"
>
{cat.name}
</button>
);
})}
</div>
</div>
</div>
</div>
)
}
export default CategoryFilter

How to re-fetch data after doing a filter in React.js with useContext

I got this component in React.js which make different kinds of filtering when I click a button, this is my code:
import React, { useContext } from 'react';
import { ModelsContext } from "../context/ModelsContext";
const FilterNav = () => {
const { modelos, guardarModelo } = useContext(ModelsContext);
const filterSegment = e => {
const segment = modelos.filter(modelo => modelo.segment === e.target.name);
guardarModelo(segment);
}
return (
<nav className="filter-container">
<div className="container">
<h3 className="filter-element-title">Filtrar por</h3>
<button type="button" className="filter-element">Todos</button>
<button type="button" className="filter-element" name="Autos" onClick={filterSegment}>Autos</button>
<button type="button" className="filter-element" name="Pickups y Comerciales" onClick={filterSegment}>Pickups y Comerciales</button>
<button type="button" className="filter-element" name="SUVs y Crossovers" onClick={filterSegment}>SUVs y Crossovers</button>
</div>
<p className="filter-element-last">Ordenar por ^</p>
</nav>
);
}
export default FilterNav;
The information I get from the api with useContext in ModelsContext.jsx, here is what I wrote so far:
import React, { createContext, useState, useEffect } from 'react';
export const ModelsContext = createContext();
const ModelsProvider = (props) => {
//State de modelos
const [modelos, guardarModelo] = useState([]);
const consultarAPI = async () => {
const api = await fetch("https://challenge.agenciaego.tech/models");
const modelos = await api.json();
guardarModelo(modelos);
}
//Cargar un modelo
useEffect(() => {
consultarAPI()
}, []);
return (
<ModelsContext.Provider
value={{
modelos,
guardarModelo
}}
>
{props.children}
</ModelsContext.Provider>
)
}
export default ModelsProvider;
My issue is that when I filter the API modelos throught the filterSegment function I don't know how to re-fetch the data from the API, because when I do a new call to the filterSegment function it filters the filtered data. I've tried to add a boolean state, and I was thinking about adding another state with allthedata, but I really lost about implementing it, I'm still very new to React.js.
I've search through stack overflow and google and I cannot get the answer, If you can give me a clue or some sort of guidance it will be appreciated.
Thanks so much!
You can add another state in the ModelsContext:
//State de modelos
const [modelos, guardarModelo] = useState([]);
const [allModelos, guardarAllModelo] = useState([]);
const consultarAPI = async () => {
const api = await fetch("https://challenge.agenciaego.tech/models");
const modelos = await api.json();
guardarAllModelo(modelos);
//uncomment if you want to have initial value for modelos state
//guardarModelo(modelos);
}
// some codes ...
<ModelsContext.Provider
value={{
allModelos,
modelos,
guardarModelo
}}
>
{props.children}
</ModelsContext.Provider>
Then in the FilterNav component:
const {allModelos, modelos, guardarModelo } = useContext(ModelsContext);
const filterSegment = e => {
const segment = allModelos.filter(modelo => modelo.segment === e.target.name);
guardarModelo(segment);
}
But this does not really re-fetch data from your web api. It just re-filters the first fetched data. if you want to re-fetch data from web api you can add consultarAPI in your context provider then call it somewhere.
Thanks code is working
This is my Portfolio gallery code First time load all data when click category then get category dataenter code here
Thanks code is working
This is my Portfolio gallery code First time load all data when click category then get category data`enter code here`
import React, { Component, useEffect, useState } from 'react'`enter code here`;
import Thumnailport_list from './Thumnailport_list';
import Portlightbox from './Portlightbox';
import Functional from './Functional';
import $ from 'jquery';
const Portfolio = () => {
const filterItem = async (categoryitem) => {
const updateitmes = allModelos.filter((curElm) => {
return curElm.categories === categoryitem
})
getporfolioState(updateitmes)
}
const [getporfolio, getporfolioState] = useState([])
const [allModelos, guardarAllModelo] = useState([]);
$(document).ready(function () {
$(".grid-wrap .grid li").unbind().click(function (e) {
console.log(this.className);
var newe = this.className;
$('.' + newe).addClass('current show');
$("#grid-gallery").addClass("slideshow-open");
});
$("#closeport").unbind().click(function (e) {
$("#grid-gallery").removeClass("slideshow-open");
$(".portfolio .grid li").removeClass('current show');
$(".portfolio .slideshow ul > li").removeClass('current show');
});
});
const portadd = () => {
document.body.classList.add('portfolio');
document.body.classList.add('at-top');
document.getElementById('port').classList.add('no-transform');
document.getElementById('port').classList.add('revealator-within');
document.getElementById('port2').classList.add('no-transform');
document.getElementById('port2').classList.add('revealator-within');
document.getElementById('navbar-collapse-toggle').classList.remove('biohidemenu');
}
const getalldata = async () => {
try {
const res = await fetch("/getdata", {
method: 'Get',
headers: {
'Content-Type': 'application/json'
}
})
const data = await res.json()
// console.log("This is our data load")
// console.log(data.portfolio)
getporfolioState(data.portfolio)
guardarAllModelo(data.portfolio)
} catch (error) {
console.log(error)
// history.push("/backoffice/login")
}
}
useEffect(() => {
getalldata()
portadd()
}, []);
return (
<>
<section id="port" class="title-section text-left text-sm-center revealator-slideup revealator-once revealator-delay1">
<h1 >my <span>portfolio</span></h1>
<span class="title-bg">works</span>
</section>
<section id="port2" className="main-content text-center revealator-slideup revealator-once revealator-delay1">
<div class="container">
<button className="btn btn-about " onClick={() => filterItem('mobileapp')}>Mobile</button>
<button className="btn btn-about " onClick={() => filterItem('frontend')}>Frontend</button>
<button className="btn btn-about " onClick={() => filterItem('gdesign')}>Graphics</button>
</div>
<div id="grid-gallery" className="container grid-gallery">
{/* Portfolio Grid Starts */}
<section className="grid-wrap">
<ul className="row grid">
{
getporfolio.map((getdata, index) => {
return (
<>
<Thumnailport_list
key={index}
portID={getdata._id}
imagetag={getdata.imguploadedFile}
figuertext={getdata.projectname}
/>
</>
)
})
}
</ul>
</section>
{/* Portfolio Grid Ends */}
{/* Portfolio Details Starts */}
<section className="slideshow" id="sdfer">
<ul>
{/* Portfolio Item Detail Starts */}
{
getporfolio.map((getdata, index) => {
return (
<>
<Portlightbox
idlight={getdata._id}
imagelight={getdata.imguploadedFile}
langport={getdata.language}
clientport={getdata.client}
projectnameport={getdata.projectname}
previewport={getdata.preview}
/>
</>
)
})
}
</ul>
{/* Portfolio Navigation Starts */}
<nav>
{/*<span className="icon nav-prev prev"><img src="images/left-arrow.png" alt="previous" /></span>
<span className="icon nav-next next"><img src="images/right-arrow.png" alt="next" /></span>*/}
<span className="nav-close" id="closeport"><img src="images/close-button.png" alt="close" /> </span>
</nav>
{/* Portfolio Navigation Ends */}
</section>
</div>
</section>
</>
)
}
export default Portfolio;

Categories

Resources