React component re-rendering to default data. Why? - reactjs

Simple app that loops through my array of birthdays which has 5 birthdays in it by default. Once I delete a birthday and click the "add birthday" button my modal pops up. When I click my "close" button my birthdays component re-renders back to 5 birthdays when I am expecting to have 4 because I deleted one upon opening my modal. Why is it doing this?
Below is my code for my birthdays component, and modal component. I have also attached an image for a visual.
App component:
import react, { useState } from "react";
import Birthdays from "./Birthdays";
import BirthayData from "./BirthdayData";
import { AddBirthdayModal, AddBirthdayButton } from "./AddBirthdayModal";
function App() {
//Keeping track of all birthdays
const [allBirthdays, setAllBirthdays] = useState([...BirthayData]);
//keeping track if add birthday modal is displayed
const [isAddBirthdayShown, setIsAddBirthdayShown] = useState(false);
//Delete selected birthday.
const DeleteBirthday = (indx) => {
const updatedBirthdays = [...allBirthdays];
updatedBirthdays.splice(indx, 1);
setAllBirthdays(updatedBirthdays);
};
return (
<>
<AddBirthdayModal
setIsAddBirthdayShown={setIsAddBirthdayShown}
isAddBirthdayShown={isAddBirthdayShown}
/>
<div className="birthdays-container">
<h2>{allBirthdays.length} Birthdays Today</h2>
<div>
{allBirthdays.length >= 1 &&
allBirthdays.map((user, index) => {
return (
<Birthdays
key={user.id}
selfie={user.image}
name={user.Name}
age={user.AgeTurning}
deleteBirthday={DeleteBirthday}
index={index}
/>
);
})}
</div>
</div>
<div>
<button className="btn" onClick={() => setAllBirthdays([])}>
Clear
</button>
<AddBirthdayButton setIsAddBirthdayShown={setIsAddBirthdayShown} />
</div>
</>
);
}
export default App;
Birthdays Component
import React from 'react'
import "./Birthday.css"
export default function Birthdays(props) {
return (
<div className="birthday-flex-container">
<div className="image-container">
<img
className="user-image center"
src={props.selfie}
alt={`Selfie of person`}
/>
</div>
<div className="user-info center">
<h4 className="name">{props.name}</h4>
<span className="age">{props.age} years</span>
</div>
<div className="delete-birthday-container">
<span onClick={() => props.deleteBirthday(props.index)}>X</span>
</div>
</div>
)
}
Modal Component
import React from "react";
import "./AddBirthdayModal.css";
function AddBirthdayModal({ setIsAddBirthdayShown, isAddBirthdayShown }) {
const showHideClassName = isAddBirthdayShown
? "modal-container display-block"
: "modal display-none";
return (
<div className={showHideClassName}>
<div className="modal">
<p className="modal-title">Who's Birthday?</p>
<div className="form-container">
<form className="birthday-form">
<input type="text" placeholder="Name" />
<br />
<input type="text" placeholder="Age" />
<br />
<label for="myfile">Select a Image: </label>
<input type="file" id="myfile" name="myfile" />
<br />
<button onClick={() => setIsAddBirthdayShown(false)}>Close</button>
</form>
</div>
</div>
</div>
);
}
function AddBirthdayButton({ setIsAddBirthdayShown }) {
return (
<div>
<button className="btn" onClick={() => setIsAddBirthdayShown(true)}>
Add Birthday
</button>
</div>
);
}
export { AddBirthdayModal, AddBirthdayButton };

There is a typo in your import:
import react, { useState } from "react";
Should be:
import React, {useState} from "react";

You are not passing the index parameter to the DeleteMyBirthday function when click delete parameter:
deleteBirthday={DeleteBirthday}
Needs to be:
deleteBirthday={() => DeleteBirthday(index)}
Like this:
import react, { useState } from "react";
import Birthdays from "./Birthdays";
import BirthayData from "./BirthdayData";
import { AddBirthdayModal, AddBirthdayButton } from "./AddBirthdayModal";
function App() {
//Keeping track of all birthdays
const [allBirthdays, setAllBirthdays] = useState([...BirthayData]);
//keeping track if add birthday modal is displayed
const [isAddBirthdayShown, setIsAddBirthdayShown] = useState(false);
//Delete selected birthday.
const DeleteBirthday = (indx) => {
const updatedBirthdays = [...allBirthdays];
updatedBirthdays.splice(indx, 1);
setAllBirthdays(updatedBirthdays);
};
return (
<>
<AddBirthdayModal
setIsAddBirthdayShown={setIsAddBirthdayShown}
isAddBirthdayShown={isAddBirthdayShown}
/>
<div className="birthdays-container">
<h2>{allBirthdays.length} Birthdays Today</h2>
<div>
{allBirthdays.length >= 1 &&
allBirthdays.map((user, index) => {
return (
<Birthdays
key={user.id}
selfie={user.image}
name={user.Name}
age={user.AgeTurning}
deleteBirthday={() => DeleteBirthday(index)}
index={index}
/>
);
})}
</div>
</div>
<div>
<button className="btn" onClick={() => setAllBirthdays([])}>
Clear
</button>
<AddBirthdayButton setIsAddBirthdayShown={setIsAddBirthdayShown} />
</div>
</>
);
}
export default App;

I was able to figure it out.
For some reason when I render my modal component like this
{isAddBirthdayShown && (
<AddBirthdayModal
setIsAddBirthdayShown={setIsAddBirthdayShown}
isAddBirthdayShown={isAddBirthdayShown}
/>
)}
Instead of
const showHideClassName = isAddBirthdayShown
? "modal-container display-block"
: "modal display-none";
It works as expected. I'm not sure why. All I do is attached a class to my modal and make it a "display:block" when isAddBirthday is true and "display: none" to hide it when its false.

Related

Add element in array by button click

I have product cards that are rendered based on a json file.
By clicking on the "Add to Cart" button, the element should be added to the array сartList, but this does not happen.
I also tried to forward the function to the component itself, but it didn’t work out too well for me.
Shop.jsx:
import React, { useState } from 'react';
import './Instruments.css';
import Cart from '../components/Cart'
import Product from '../components/Product'
import cart from '../img/cart.png';
import data from "../data/data.json";
unction Shop() {
const [value, setValue] = useState('');
const [currentData, setCurrentData] = useState(data);
const [cartList, setCartList] = useState([]);
return (
<div className='shop'>
<div className='container'>
<div className='shop__main-products'>
{
currentData.filter((el) => {
return value.toLowerCase() === '' ? el : el.title.toLowerCase().includes(value.toLowerCase())
}).map((el, index) => {
return (
<Product img={el.img} title={el.title} price={el.price} key={el.id} onClick={() => setCartList([...cartList, el])}/>
)
})
}
</div>
</div>
<Cart active={modalActive} setActive={modalSetActive}/>
</div>
</div>
);
}
export default Shop;
Product.jsx:
import React, { useState } from 'react';
import './Product.css';
function Product({img, title, price, id, type}) {
return (
<div className='product' key={id} type={type}>
<div className='buy__top'>
<div className='product__top-image-background'>
<img className='product__top-image' src={img}></img>
</div>
<h3 className='product__top-title'>{title}</h3>
</div>
<div className='product__buy'>
<h3 className='product__buy-price'>{price} грн</h3>
<button className='product__buy-button'>В корзину</button>
</div>
</div>
)
}
export default Product;
It looks like the issue is with how you're passing the onClick function to the Product component. The onClick prop should be passed to the "Add to Cart" button, not the Product component itself. You should change the following line:
<Product img={el.img} title={el.title} price={el.price} key={el.id} addToCart={() => setCartList([...cartList, el])}/>
And in the Product component, you should add the onClick prop to the "Add to Cart" button:
<button className='product__buy-button' onClick={addToCart}>В корзину</button>
This way, when the button is clicked, it will call the addToCart function and add the element to the cartList array.
You are not adding the onClick function to the props of the Product component pass it down the pipe and itll work.
function Product({img, title, price, id, type, onClick}) {
return (
<div className='product' key={id} type={type}>
<div className='buy__top'>
<div className='product__top-image-background'>
<img className='product__top-image' src={img}></img>
</div>
<h3 className='product__top-title'>{title}</h3>
</div>
<div className='product__buy'>
<h3 className='product__buy-price'>{price} грн</h3>
<button className='product__buy-button' onClick={onClick}>В корзину</button>
</div>
</div>
)
}

Pass information from an input to another component in another file

I'm having a problem, it's been a few days, I'm studying about React and Typescript and I'm developing a temperature application, I'm stopped in a part, where I want the user to click on the submit form, the information that was typed in the input is passed to another component.
Follow my two codes below
CityWeatherSearch.tsx
import { MagnifyingGlass } from 'phosphor-react'
import { FormEvent, useCallback, useRef, useState } from 'react';
import * as Styled from './style'
export function CityWeatherSearch(){
const inputRef = useRef<HTMLInputElement>(null);
const [city,setCity] = useState('');
function handleClick(event:FormEvent) {
event.preventDefault();
const inputCity = inputRef?.current?.value;
console.log({
inputCity, city
});
}
return(
<>
<Styled.BoxSearchCity>
<div className="headerSearch">
<form>
<input type="text" placeholder='Procurar Cidade...' ref={inputRef} onChange={
event => setCity(event.target.value)} />
<button type="submit" onClick={handleClick}>
<MagnifyingGlass/>
</button>
</form>
</div>
<div className="bodySearch">
{city}
</div>
</Styled.BoxSearchCity>
</>
)
}
MainWeatherLive.tsx
import {Clock} from 'phosphor-react'
import { useFetch } from '../../GetData/useFetch'
import * as Styled from './style'
type DataWeather = {
name: string,
condition:{
text:string,
icon:string
},
temp_c:number,
hour:[{
temp_c:number,
time:string,
condition:{
text:string,
icon:string
}
}]
}
export function MainWeatherLive(){
const {dataLocation: dataWeatherApi, isFetching, dataCurrent:dataCurrentApi, dataForecast:forecastApi}
= useFetch<DataWeather>('/v1/forecast.json?key=aff6fe0e7f5d4f3fa0611008221406&q=Guarulhos?days=1&aqi=no&alerts=no');
return(
<>
<Styled.HeaderBox>
<h6>Weather Now</h6>
</Styled.HeaderBox>
<Styled.Container>
{isFetching &&
<p>Carregando...</p>
}
<div className="mainInformation">
<div className="temperatura">
<span>{dataCurrentApi?.temp_c}º</span>
</div>
<div>
A cidade é {cityName}
</div>
<div className="boxCidade">
<div className="cidade">
<span>{dataWeatherApi?.name}</span>
</div>
<div className="tempoHoras">
<span>
{new Date().toLocaleTimeString('pt-BR',{hour12:false, hour:'numeric',minute:'numeric'})} - {new Date().toLocaleDateString()}
</span>
</div>
</div>
<div className="iconeTem">
<img src={dataCurrentApi?.condition.icon} alt={dataCurrentApi?.condition.text} />
</div>
</div>
<div className="footerBox">
<div className="headerFooter">
<Clock/>
<span>Horários</span>
</div>
<div className="listaHorarios">
<ul className="boxTT">
{
forecastApi?.hour?.map(weatherA =>{
const hourTemp = weatherA.time.split(" ")[1].replace(":00","");
const hourTempNumber:number = +hourTemp;
const hourNow = new Date().getHours();
return(
<>
{
hourTempNumber == hourNow &&
<li>
<div className="titulo" key={weatherA.temp_c}>
<span>{hourTempNumber}</span>
</div>
<div className="temperatura">
<img src={weatherA.condition.icon} alt={weatherA.condition.text} />
<span>{dataCurrentApi?.temp_c}º</span>
</div>
</li>
}
{
hourTempNumber > hourNow &&
<li>
<div className="titulo" key={weatherA.temp_c}>
<span>{hourTempNumber}</span>
</div>
<div className="temperatura">
<img src={weatherA.condition.icon} alt={weatherA.condition.text} />
<span>{weatherA.temp_c}º</span>
</div>
</li>
}
</>
)
})
}
</ul>
</div>
</div>
</Styled.Container>
</>
)
}
Weather.tsx
import { CityWeatherSearch } from "./WeatherC/CityWeatherSearch";
import { MainWeatherLive } from "./WeatherC/MainWeatherLive";
import { WeatherDetails } from "./WeatherC/WeatherDetails";
import coldImage from '../assets/cold.jpg'
import sunImage from '../assets/sun.jpg'
import rainImage from '../assets/rain.jpg'
import nightVideo from '../assets/night.mp4'
import night from '../assets/night.jpg'
export const TypesWeather = {
NIGHT:{
video:{
source: nightVideo
},
image:{
source: night
}
},
OVERCAST:{
video:{
source: nightVideo
},
image:{
source: night
}
},
COLD:{
image:{
source: coldImage,
title: 'Frio'
}
},
SUN:{
image:{
source: sunImage,
title: 'Verão'
}
},
RAIN:{
image:{
source: rainImage,
title: 'Chuva'
}
},
};
export type TypesWeatherV2 = keyof typeof TypesWeather;
export function Weather(){
return (
<>
<div className="globalSite" style={{background:`linear-gradient(to bottom,rgba(0,0,0,.85) 0,rgba(0,0,0,.85) 100%),url(${TypesWeather.RAIN.image.source})`}}>
</div>
<div className="boxAllWeather">
<div className="backgroundWeather" style={{backgroundImage:`url(${TypesWeather.RAIN.image.source})`}}></div>
<div className="boxAllInff">
<div className="mainWeather">
<MainWeatherLive />
</div>
<div className="otherInfoWeather">
<CityWeatherSearch />
<WeatherDetails />
</div>
</div>
</div>
</>
)
}
I want to pass the city typed in CityWeatherSearch.tsx to MainWeatherLive.tsx. Where is the space 'A cidade é {cityName}' reserved, I've tried everything, but I haven't been able to, could you help me?
You can do this in several ways:
parent -> child : use props
child -> parent : use callback/event emitter
no direct relationship : consider using state management tool like
redux
Just lift your state uo to the parent component and pass if to the cild components as props:
function WeatherPage() {
const [city,setCity] = useState('');
return (
<>
<CityWeatherSearch city={city} setCity={setCity}/>
//...
<MainWeatherLive city={city}/>
//...
</>
)
}
function CityWeatherSearch({city, setCity}) {
// your code here, only without const [city, setCity] useState()
}
function MainWeatherLive({city}) {
// your code here, now you can access city
}
If your two components don't have a direct common parent and you don't want to pass down city and setCity through a deep component hierarchy, think about using useContext to share state within your application.

how do I get my add to cart button to work in React (cart is not iterable) issue comes up in console?

When I click my add to cart button on the page it comes up with the error 'cart is not iterable' in console not sure what Im doing wrong as Im not sure I really understand the error. I have a separate shop and cart page. The shop has ad addtoCart button which works Thanks in Advance.
import React, { useState } from 'react';
import './Product.css';
import './Imagegallery.js';
export default function Product(product, cart, setCart){
const prod=product
const addToCart = (product) => {
let newCart = [...cart];
let itemInCart = newCart.find(
(item) => prod.product.name === item.name
);
if (itemInCart) {
itemInCart.quantity++;
} else {
itemInCart = {
...product,
quantity: 1,
};
newCart.push(itemInCart);
}
setCart(newCart);
};
return (
<>
<h1>Product Page</h1>
<div className="products">
<div className="product">
<h3>{prod.product.name}</h3>
<h4>£{prod.product.cost}</h4>
<p>{prod.product.description}</p>
<br />
<button onClick={() => addToCart(product)}>
Add to Cart
</button>
<section>
<div className="image-gallery">
<img className="gallery-highlight" img src={prod.product.image} alt=.
{prod.product.name} />
<div className="image-preview">
<img src={prod.product.image2} className="image-active" />
<img src={prod.product.image3} />
<br />
</div>
</div>
</section>
</div>
</div>
</>
);
}

Use same usestate hook for multiple icons

I have a two buttons in my react project (material ui icons) which are lowerarrow(both) and i made a use state and function to change upper arrow to lower arrow when it is clicked but i dont know how to use the same state for my another icon too, maybe using some id or something i dont know , right now i put {iconstate} in both the icons so both the icons are changing together i am trying to figure out how to change them individually without making new state and function here is my code :-
JSX:-
import React , {useState} from 'react';
import Weather_leftpanecss from './Weather_leftpane.module.css'
import KeyboardArrowDownIcon from '#mui/icons-material/KeyboardArrowDown';
import KeyboardArrowUpIcon from '#mui/icons-material/KeyboardArrowUp';
export default function Weather_leftpane() {
const [iconstate, seticonstate] = useState(<KeyboardArrowDownIcon/>)
const [valuestate, setvaluestate] = useState(true)
const togglearrow = ()=>{
if(valuestate==true){
seticonstate(<KeyboardArrowUpIcon/>)
setvaluestate(false)
}
else{
seticonstate(<KeyboardArrowDownIcon/>)
setvaluestate(true)
}
}
return <div>
<div className={Weather_leftpanecss.main}>
<div id={Weather_leftpanecss.item_1}>Weather</div>
<div id={Weather_leftpanecss.item_2}>
<input type="text" placeholder='Search for city..' />
</div>
<div id={Weather_leftpanecss.item_3}>
<div className={Weather_leftpanecss.item_3_content} id="item_3_1">
Cities
</div>
<div className={Weather_leftpanecss.item_3_content} id="item_3_2" onClick={togglearrow} >
{iconstate}
</div>
</div>
<div id={Weather_leftpanecss.item_4}>
<div className={Weather_leftpanecss.item_4_content} id="item_4_1">
Settings
</div>
<div className={Weather_leftpanecss.item_4_content} id="item_4_2" onClick={togglearrow}>
{iconstate}
</div>
</div>
</div>
</div>;
}
css:-
.App {
font-family: sans-serif;
text-align: center;
}
(not the real css ,just added it because i have included it everywhere in my code with module)
Here you need to save the selected Id in the state.
import React , {useState} from 'react';
import Weather_leftpanecss from './Weather_leftpane.module.css'
import KeyboardArrowDownIcon from '#mui/icons-material/KeyboardArrowDown';
import KeyboardArrowUpIcon from '#mui/icons-material/KeyboardArrowUp';
export default function Weather_leftpane() {
const [valuestate, setvaluestate] = useState(true)
const [id, setId] = useState(null);
const togglearrow = (val)=>{
if(valuestate==true){
setvaluestate(false)
}else{
setvaluestate(true)
}
setId(val);
}
return <div>
<div className={Weather_leftpanecss.main}>
<div id={Weather_leftpanecss.item_1}>Weather</div>
<div id={Weather_leftpanecss.item_2}>
<input type="text" placeholder='Search for city..' />
</div>
<div id={Weather_leftpanecss.item_3}>
<div className={Weather_leftpanecss.item_3_content} id="item_3_1">
Cities
</div>
<div className={Weather_leftpanecss.item_3_content} id="item_3_2" onClick={() => togglearrow('item_3_2')} >
{valuestate && id == 'item_3_2' ? KeyboardArrowUpIcon : KeyboardArrowDownIcon }
</div>
</div>
<div id={Weather_leftpanecss.item_4}>
<div className={Weather_leftpanecss.item_4_content} id="item_4_1">
Settings
</div>
<div className={Weather_leftpanecss.item_4_content} id="item_4_2" onClick={() => togglearrow('item_4_1')}>
{valuestate && id == 'item_4_1' ? KeyboardArrowUpIcon : KeyboardArrowDownIcon }
</div>
</div>
</div>
</div>;
}
Here you can use one state to achieve that, saving in one state the status of both buttons, which one is clicked or not and render the icon based on that status.
the toggleArrow function gets the itemId and uses it to set the updated value of the button. We use !prevState[itemId] since if it is false it will become true and vise versa.
I took the liberty of giving the state a more informative name than a generic name.
import React, { useState } from "react";
import Weather_leftpanecss from "./Weather_leftpane.module.css";
import KeyboardArrowDownIcon from "#mui/icons-material/KeyboardArrowDown";
import KeyboardArrowUpIcon from "#mui/icons-material/KeyboardArrowUp";
export default function Weather_leftpane() {
const [clickedButtons, setClickedButtons] = useState({
item_3_2: false,
item_4_2: false
});
const toggleArrow = (itemId) => {
setClickedButtons((prevState) => ({
...prevState,
[itemId]: !prevState[itemId]
}));
};
return (
<div>
<div className={Weather_leftpanecss.main}>
<div id={Weather_leftpanecss.item_1}>Weather</div>
<div id={Weather_leftpanecss.item_2}>
<input type="text" placeholder="Search for city.." />
</div>
<div id={Weather_leftpanecss.item_3}>
<div className={Weather_leftpanecss.item_3_content} id="item_3_1">
Cities
</div>
<div
className={Weather_leftpanecss.item_3_content}
id="item_3_2"
onClick={() => toggleArrow("item_3_2")}
>
{clickedButtons["item_3_2"] ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</div>
</div>
<div id={Weather_leftpanecss.item_4}>
<div className={Weather_leftpanecss.item_4_content} id="item_4_1">
Settings
</div>
<div
className={Weather_leftpanecss.item_4_content}
id="item_4_2"
onClick={() => toggleArrow("item_4_2")}
>
{clickedButtons["item_4_2"] ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</div>
</div>
</div>
</div>
);
}

Countries End point is not returning anything although works in sandbox

I am building a small Countries App in React. The issue I have is that I have changed the endpoint and now it is not returning anything. I checked this in Sandbox and it worked fine. Below is the code for the page that renders the details of the country.
import React from 'react';
import { useEffect } from "react";
import { useState } from "react";
import { NavBarCard } from '../NavBar/NavBarCard';
import './DetailCountryCard.css';
import {Link} from 'react-router-dom';
function DetailCountryCard ({ match }) {
useEffect(() => {
fetchItem();
console.log(match);
// eslint-disable-next-line
}, []);
const [country, setCountry] = useState({});
const [darkMode, setDarkMode] = useState(false);
const fetchItem = async () => {
const fetchItem = await fetch(
`https://restcountries.eu/rest/v2/alpha/${match.params.alpha3Code}`
);
const country = await fetchItem.json();
setCountry(country);
console.log(country);
};
return (
// <div>
// <h1>the country</h1>
// </div>
<div className={darkMode ? "dark-mode" : "light-mode" }>
<NavBarCard handlechange={()=> setDarkMode(prevMode => !prevMode)} moonMode={darkMode ? "moon fas fa-moon" :
"moon far fa-moon"}
darkMode={darkMode ? "dark-mode" : "light-mode"}/>
<div className="detailCard">
<Link to="/">
<button className="topButton myButton">Back</button>
</Link>
<div className="container">
<img className="flag" alt="flag" src={country.flag} />
<div className="countryName">
<div className="NativeName">
<h1 className="Country">{country.name}</h1>
<h2>Native Name:</h2>
<p> {country.nativeName}</p>
<br />
<h2>Population:</h2>
<p> {country.population}</p>
<br />
<h2>Region:</h2>
<p> {country.region}</p>
<br />
<h2>Sub Region:</h2>
<p> {country.subregion}</p>
<br />
<h2>Capital:</h2>
<p> {country.capital}</p>
<br />
<div>
<h2>Border Countries:</h2>{country.borders && country.borders.map(function(border){
return(
<Link to={`/DetailCard/${border}`}>
<button className="myButton"> {border} </button>
</Link>
)
})}
</div>
</div>
</div>
<div className="domain">
<h2>Top Level Domain: </h2>
<p>{country.topLevelDomain}</p>
<br />
<h2>Currencies: </h2>
<p>{country.currencies && country.currencies.map(({ name }) => name).join(", ")}</p>
<br />
<h2>Languages: </h2>
<p>{country.languages && country.languages.map(({ name }) => name).join(", ")}</p>
<br />
</div>
</div>
</div>
</div>
);
}
export default DetailCountryCard;
All I get is blank page and in the console it saying that I am getting a status of 400. Any help would be appreciated. If I just put bra as the last three letters of the alpha code this is what I get.

Resources