Here I am making a shopping app and I have a working cart in it and below is my code for my cart component and here in cart I want to render order button conditionally for that I have isFound state and first I am getting data from my redux store and then I am checking below in useEffect hook if my list is not empty(list is const where I am storing my redux fetched data) then I will set my state=true and initially it is false but the problem is that useEffect is chanigng state to true if there is nothing inside of my list const means even if cart is empty and even though I am setting useEfect dependency proprly as well but it is showing order button all the time so someone can please help thanks:
import { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import classes from "./Cart.module.css";
const Cart = () => {
const navigate = useNavigate();
const [isFound, setIsFound] = useState(false);
const orders = useSelector((state) => state.data.DUMMY_DATA);
const list = orders.map(
(data, key) =>
data.product_count > 0 && (
<div className={classes.wrapper}>
<div className={classes.item}>
Item: {data.product_name}{" "}
</div>
<div className={classes.amount}>
Amount: {data.product_count}{" "}
</div>
<div className={classes.price}>
Price: {data.product_price}
</div>
</div>
)
);
useEffect(() => {
if (list !== "") {
setIsFound(true);
}
}, [list]);
return (
<div className={classes.modal}>
<div className={classes.root}>
<span
className={classes.close}
onClick={() => navigate("/", { replace: true })}
>
×
</span>
{list}
{isFound && (
<div className={classes.order_button_wrapper}>
<button className={classes.order_button}>Order</button>
</div>
)}
</div>
</div>
);
};
export default Cart;
.map alway return an array. So list !== "" will alway be true.
Here is useEffect, you have an array not a string as list value:
useEffect(() => {
if (list.length > 0) {
setIsFound(true);
}
}, [list]);
You have placed a watcher on the list variable, that's why useEffect is not calling, you need to place a watcher on the state because the state is being rendered and when any changes to the state useEffect will be called, variable is not rendered That's is why useEffect is not being called and changes to your component are not being replicated. You have to create a state and put the list value in the state and you have to call the function in the useEffect because you only have one called otherwise your function will be calling, as you code below can see.
import { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import classes from "./Cart.module.css";
const Cart = () => {
const navigate = useNavigate();
const [isFound, setIsFound] = useState(false);
const orders = useSelector((state) => state.data.DUMMY_DATA);
const [ordersList, setOrdersList] = useState("");
useEffect(() => {
const list = orders.map(
(data, key) =>
data.product_count > 0 && (
<div className={classes.wrapper}>
<div className={classes.item}>
Item: {data.product_name}{" "}
</div>
<div className={classes.amount}>
Amount: {data.product_count}{" "}
</div>
<div className={classes.price}>
Price: {data.product_price}
</div>
</div>
)
);
setOrdersList(list);
}, [])
useEffect(() => {
if (ordersList !== "") {
setIsFound(true);
}
}, [ordersList]);
return (
<div className={classes.modal}>
<div className={classes.root}>
<span
className={classes.close}
onClick={() => navigate("/", { replace: true })}
>
×
</span>
{ordersList}
{isFound && (
<div className={classes.order_button_wrapper}>
<button className={classes.order_button}>Order</button>
</div>
)}
</div>
</div>
);
};
export default Cart;
Related
I need to toggle multiple blocks with true/false in react state, but true and false works for all blocks at the same time.
import { useState } from "react";
...
const [toggleThisElement, setToggleThisElement] = useState(false);
...
{
data.map((d, id) => {
return (
<div className="single-history" key={id}>
<div className="h-head">
click this div for toggle h-info block
</div>
<div className="h-info">toggling block</div>
</div>
)
})
}
Currently, all your toggle items share the same state. To avoid that create a separate component for toggling logic called ToggleItem as below.
import { useState } from "react";
const ToggleItem = ({ discription, id }) => {
const [toggleThisElement, setToggleThisElement] = useState(false);
return (
<div className="single-history" key={id}>
<button
className="h-head"
onClick={() => setToggleThisElement((prev) => !prev)}
>
click this btn for toggle h-info block {id}
</button>
{toggleThisElement && <div className="h-info">{discription}</div>}
</div>
);
};
export default function App() {
const data = ["first", "second", "third"];
return (
<>
{data.map((d, id) => {
return <ToggleItem id={id} discription={d} />;
})}
</>
);
}
I'm new to react.js and I want to apply the toggle feature at 'place-box' by using 'isOpen' state and my intention is it only works when I click single place-box div so I added onClick event at 'place-box' div. but all of the elements are toggled at the same time.
I guess it's because they all have the same class name.
how can I fix this?
import React, { useState, useEffect } from "react";
import { useQuery } from "#apollo/client";
import { FETCH_CITIES_QUERY } from "../../server/Data/RentQueries";
import PlaceResult from "../Rent/PlaceResult";
const CityResult = (props) => {
const [placeId, setPlaceId] = useState();
const [isOpen, setIsOpen] = useState(false);
const { loading, error, data } = useQuery(FETCH_CITIES_QUERY, {
variables: { cityName: cityName },
});
const showPlaceInfo = (placeId, e) => {
e.preventDefault();
setPlaceId(placeId);
setIsOpen((isOpen) => !isOpen);
};
return (
<div>
{data &&
data.cities.map((city) => {
return (
<div className="city-box">
{city.places.map((place) => {
return (
// this is place-box div and I added onClick event here
<div
className="place-box"
key={place.id}
onClick={(e) => {
e.stopPropagation();
showPlaceInfo(place.id, e);
}}
>
<li className="place-name">{place.name}</li>
{isOpen && (
<PlaceResult className="place-indiv" placeId={placeId} />
)}
{!isOpen && (
<div className="place-info-box">
<li>{place.address}</li>
{conditionCheck(city.condition)}
<li>{place.phone}</li>
</div>
)}
</div>
);
})}
</div>
);
})}
</div>
);
};
export default CityResult;
Your variable isOpen is used for all cities. If you change isOpen to true all place-boxes are opened. You should store the id of the currently opened city inside a variable and compare against it to check if the current city in the for loop should be opened.
import React, { useState, useEffect } from "react";
import { useQuery } from "#apollo/client";
import { FETCH_CITIES_QUERY } from "../../server/Data/RentQueries";
import PlaceResult from "../Rent/PlaceResult";
const CityResult = (props) => {
const [placeId, setPlaceId] = useState();
const [openedPlaceId, setOpenedPlaceId] = useState(undefined);
const { loading, error, data } = useQuery(FETCH_CITIES_QUERY, {
variables: { cityName: cityName },
});
const showPlaceInfo = (placeId, e) => {
e.preventDefault();
setPlaceId(placeId);
setOpenedPlaceId(placeId);
};
return (
<div>
{data &&
data.cities.map((city) => {
return (
<div className="city-box">
{city.places.map((place) => {
return (
// this is place-box div and I added onClick event here
<div
className="place-box"
key={place.id}
onClick={(e) => {
e.stopPropagation();
showPlaceInfo(place.id, e);
}}
>
<li className="place-name">{place.name}</li>
{openedPlaceId === place.id && (
<PlaceResult className="place-indiv" placeId={placeId} />
)}
{!(openedPlaceId === place.id) && (
<div className="place-info-box">
<li>{place.address}</li>
{conditionCheck(city.condition)}
<li>{place.phone}</li>
</div>
)}
</div>
);
})}
</div>
);
})}
</div>
);
};
export default CityResult;
This way only the clicked place will be opened.
The render function does not get called twice, but instead the elements of a list are duplicated, and after a refresh of the application everything works just fine and the duplicates disappear. It seems to be a corner case but I can't get my head around it. Also worth mentioning that once an element is deleted from the list, both the element and its duplicate dissapear.
What could possibly cause such a bug?
import React, { useContext, useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import {
fetchSavedInternships,
selectAllSavedInternships,
} from "./savedInternshipsSlice";
import { fetchInternships, selectAllInternships } from "../Anonymous/internshipsSlice";
import Loading from "../Universal/Loading";
import InternshipCard from "../Anonymous/InternshipCard";
import { v4 as uuidv4 } from "uuid";
import TabMenu from "../Universal/TabMenu";
const SavedInternships = () => {
const dispatch = useDispatch();
const savedInternships = useSelector(selectAllSavedInternships);
const statusSavedInternships = useSelector((state) => state.savedInternships.status);
const errorSavedInternships = useSelector((state) => state.savedInternships.error);
const internships = useSelector(selectAllInternships);
const statusInternships = useSelector((state) => state.internships.status);
const errorInternships = useSelector((state) => state.internships.error);
useEffect(() => {
let user = JSON.parse(sessionStorage.getItem("user"));
if (statusSavedInternships === "idle") {
dispatch(fetchSavedInternships(user.id));
}
if (statusInternships === "idle") {
dispatch(fetchInternships);
}
console.log("savedInternships");
}, []);
return (
<>
<TabMenu />
<br />
{(statusSavedInternships === "loading" || statusInternships === "loading") && (
<Loading />
)}
{statusSavedInternships === "succeeded" && (
<>
{savedInternships.length > 0 ? (
<div className="container">
<div className="row text-center ml-1">
<h5>Stagii salvate</h5>
</div>
<div className="row row-cols-2 row-cols-md-3 row-cols-lg-4">
{savedInternships.map((savedInternship) => (
<div key={uuidv4()} className="mb-3 col">
<div className="card">
<InternshipCard
internshipId={savedInternship.internshipId}
companyId={
internships.find((c) => c.id === savedInternship.internshipId)
?.companyId
}
/>
</div>
</div>
))}
</div>
</div>
) : (
<div className="text-center text-muted">Nu ai salvat niciun stagiu</div>
)}
</>
)}
{statusSavedInternships === "error" && <div>{errorSavedInternships}</div>}
</>
);
};
export default SavedInternships;
You can either check the value of savedInternships on first render and resolve issues with state part if there is any or you can implicitly put the respective dependencies you are checking in the useEffect, here you can check the mutation of statusSavedInternships and statusInternships prior getting into useEffect.
I am new to ReactJS and I would like to ask how you can reset a useState when it reaches a specific condition.
In my code I basically have a dropdown selection that returns data based on its selected value.
I can display this with no problem, and was also able to add a 'load more' button to update the limit to request from the API.
My problem now is I want to reset it back to the limit of 10 as I change my dropdown value to something else.
My dropdown code looks like this
import React, { useContext, useState } from 'react';
import { BreedProvider } from './BreedContext';
import { BreedsContext } from './BreedsContext';
import Cats from './Cats';
const BreedSelection = () => {
const [breeds] = useContext(BreedsContext);
const [cat, setCat] = useState([]);
const [limit, setLimit] = useState(10);
function handleChange(e) {
setCat(e.target.value);
setLimit(limit);
}
return (
<>
<div className="row">
<div className="col-md-3 col-sm-6 col-12">
<div className="form-group">
<label className="form-label" htmlFor="breed">Breed</label>
<select className="form-control" onChange={handleChange} disabled={breeds.length === 0}>
<option value={null}>Select Breed</option>
{breeds.map(breed => (
<option key={breed.id} value={breed.id}>{breed.name}</option>
))}
</select>
</div>
</div>
</div>
<BreedProvider breed={cat} limit={limit}>
<Cats />
</BreedProvider>
</>
);
}
export default BreedSelection;
After which, this then calls my context to display the results and allow the user to load more via a button
import React, { useState, useEffect, createContext } from 'react';
import axios from '../services/axios';
import requests from '../services/requests';
export const BreedContext = createContext();
export const BreedProvider = ({ children, breed, limit }) => {
const [breeds, setBreeds] = useState([]);
const [showMore, setShowMore] = useState(limit);
useEffect(() => {
async function fetchData() {
if (breed.length !== 0) {
const request = await axios.get(requests.fetchBreed + 'page=1&limit=' + showMore + '&breed_id=' + breed);
setBreeds(request.data);
console.log('async', request.data.length);
return request;
}
}
fetchData();
}, [breed, showMore]);
return (
<BreedContext.Provider value={[breeds, setBreeds]}>
{children}
<div className="row">
<div className="col-md-3 col-sm-6 col-12">
<button type="button" className="btn btn-success" onClick={() => { setShowMore(showMore + limit) }}>Load more</button>
</div>
</div>
</BreedContext.Provider>
);
}
My problem here now is how do you reset the limit to 10 after you change your dropdown values? ReactJS is very new to me and I just learned this about 2 days ago.
hello I did the edit below to reset show More when breed is changed
import React, { useState, useEffect, createContext } from 'react';
import axios from '../services/axios';
import requests from '../services/requests';
export const BreedContext = createContext();
export const BreedProvider = ({ children, breed, limit,resetLimit}) => {
const [breeds, setBreeds] = useState([]);
const [showMore, setShowMore] = useState(limit);
useEffect(() => {
if (breed.length !== 0) {
setShowMore(limit);
}
}, [breed]);
useEffect(() => {
async function fetchData() {
if (breed.length !== 0) {
const request = await axios.get(requests.fetchBreed + 'page=1&limit=' + showMore + '&breed_id=' + breed);
setBreeds(request.data);
console.log('async', request.data.length);
return request;
}
}
fetchData();
}, [showMore,breed]);
return (
<BreedContext.Provider value={[breeds, setBreeds]}>
{children}
<div className="row">
<div className="col-md-3 col-sm-6 col-12">
<button type="button" className="btn btn-success" onClick={() => { setShowMore(showMore + limit) }}>Load more</button>
</div>
</div>
</BreedContext.Provider>
);
}
I'm trying to simply pass the Id of a clicked item to display on another page under a different component ("Cart") . At the bottom of the code below, I have a button containing <Cart test={product.id} /> which extracts the Id that I want to be displayed in "Cart" when the button is clicked.
However, I am instead getting an error message of:
Objects are not valid as a React child (found: object with keys
{history, location, match, staticContext}). If you meant to render a
collection of children, use an array instead.
Is there a simple syntax error?
import React, { useState, useEffect, Cart } from 'react';
import './../App.css';
import * as ReactBootStrap from 'react-bootstrap';
function Item(props) {
const [product, setProduct] = useState([]);
const [loading, setLoading] = useState(false);
const [quantity, setQuantity] = useState(1);
const [cost, setCost] = useState([]);
useEffect(async () => {
fetchItems();
}, []);
const itemId = props.match.params.item;
const fetchItems = async () => {
const data = await fetch('https://fakestoreapi.com/products/' + itemId);
const items = await data.json();
setProduct(items);
setLoading(true);
setCost(items.price);
};
function priceUSD(change) {
return change.toFixed(2);
}
useEffect(() => {
const newCost = quantity * product.price;
setCost(priceUSD(newCost));
}, [quantity]);
return (
<div className="App">
<h2>Item</h2>
<div className="gridContainer">
{loading ? (
<div key={itemId} className="productStyle">
<img src={product.image} className="productImage"></img>
<p>{product.title}</p>
<p>{product.description}}</p>
<p>${priceUSD(product.price)}</p>
<div className="quantity">
<button
className="btn minus-btn"
type="button"
onClick={quantity > 1 ? () => setQuantity(quantity - 1) : null}
>
-
</button>
<input type="text" id="quantity" value={quantity} />
<button className="btn plus-btn" type="button" onClick={() => setQuantity(quantity + 1)}>
+
</button>
</div>
<button type="button" onClick={() => <Cart test={product.id} />}>
Add to shopping cart ${cost}
</button>
</div>
) : (
<ReactBootStrap.Spinner className="spinner" animation="border" />
)}
</div>
</div>
);
}
export default Item;
Cart
import React, { useState, Item } from 'react';
import './../App.css';
import './Item.js';
function Cart(test) {
return (
<div className="App">
<p>{test}</p>
</div>
);
}
export default Cart;
Component props are objects. You can read more about them in the official documentation.
You can either destructure the props of the Cart component:
function Cart({test}) {
return (
<div className="App">
<p>{test}</p>
</div>
);
}
or use explicitly test property of props:
function Cart(props) {
return (
<div className="App">
<p>{props.test}</p>
</div>
);
}