implementing local storage in react application - javascript

I am creating a challenge tracking app in React. I would like to, after clicking on the challenge button and approving it, be able to add it and save it to the local storage (as a value to save the name of the chosen challenge) and later to print it in the dashboard.
Could anyone please help me with that.
I have 3 classes I am working now and will paste them below.
ChooseChallenge.js
function Challange() {
const [isPopped, setPop] = useState(false);
const pop = () => {
setPop(!isPopped);
};
return (
//Fragments
<>
{isPopped && <Dialog />}
<div className="chooseChallenge">
{/* <Leaf/> */}
<h1 className="newchallenge">New Challange</h1>
<hr />
<div className="challanges">
<button className="challangeBtn" onClick={pop}>
Eat Vegetarian (31days)
</button>
<button className="challangeBtn" onClick={pop}>
Take the bike to work (14days)
</button>
<button className="challangeBtn" onClick={pop}>
Recycle your plastic bottles (31days)
</button>
<button className="challangeBtn" onClick={pop} >
Use public transport to commute (31days)
</button>
<button className="challangeBtn" onClick={pop}>
Don't fly an airplane (365days)
</button>
</div>
<br />
</div>
</>
);
}
export default Challange;
Dialog.js
function Dialog (){
const [isOpen, setOpennes] = useState(true);
const Close = () => {
setOpennes(false);
}
const [value, setValue] = React.useState(
localStorage.getItem('challengeName') || ''
);
React.useEffect(() => {
localStorage.setItem('challengeName', value);
}, [value]);
const onChange = event => setValue(event.target.value);
return(
<div className={isOpen ? 'dialogBox' : 'dialogHide'}>
<h3 id="header">Do you accept the challange?</h3>
<div className="approvalButtons">
<button className= "approvalButton" onClick = {Close} value={value} onChange={onChange}> Approve </button>
<button className= "approvalButton" onClick = {Close}> Decline </button>
</div>
</div>
)
}
export default Dialog;
Dashboard.js
export default function Dashboard() {
// const challengelist = document.querySelector('#challange-list')
const [challs, setChalls] = useState([]);
useEffect(() => {
const fetchData = async () => {
var challs = [];
await database
.collection("Challenges")
.get()
.then((snapshot) => {
snapshot.docs.forEach((doc) => {
challs.push(doc.data().ChallengeName);
});
});
setChalls(challs);
};
fetchData();
}, []);
return (
<div className="Dashboard">
<Header />
<div className="circle">
<img id="leafpicture" src={leafpic} alt="eco-picture" />
<div className="textIn">
<h1> You saved </h1>
<h5>0.00 CO2</h5>
</div>
</div>
<div>
<ul id="challange-list">
{challs.map((ch) => (
<li key={ch}>{ch}</li>
))}
</ul>
</div>
<div className="progressbar">
<h3>Track your challenges!</h3>
{testData.map((item, idx) => (
<ProgressBar
key={idx}
bgcolor={item.bgcolor}
completed={item.completed}
/>
))}
</div>
<br />
</div>
);
}

on dialog.js the value of the button starts with an empty string and this value never changes, so you are always storing and empty string.

Related

How to preserve state of flip card after navigating to another page and then coming back to same page

I tried to preserve the state of flip card, when I flip card I navigated to another route and after coming back from that page the card again come back to its original state, I want to preserve the flipped card state(back side).here is the code first component that renders is Cards and second one is FlippableCard and third one is Cards.
const Cards = () => {
return (
<>
<Navbar></Navbar>
<div className="round-box">Flip a Card</div>
<div className="flex-container">
<Remaincard />
<div className="flex-container-child">
<div className="flex-child">
<FlippableCard title={data[0].cardName} key={0} />
</div>
</div>
</div>
</>
);
};
export default Cards;
function Card({ onClick, title }) {
const navigate = useNavigate();
const timeOutFun = (e) => {
setTimeout(() => navigate("../afterflip/" + title), 300);
console.log(title);
};
return (
<>
<div className="card" onClick={onClick}>
<div className="card-back"></div>
<div className="card-front">
<button
className="middle card-front"
onClick={() => {
timeOutFun();
}}
>
hiii
</button>
<p
onClick={() => {
timeOutFun();
}}
className="text-on-card"
>
{title}
</p>
</div>
</div>
</>
);
}
function FlippableCard({ title, key }) {
const [showFront, setShowFront] = useState(true);
// console.log("showFront");
const [color, setColor] = useState("#110781");
return (
<div className="scroll-remove">
<div className="flippable-card-container">
<CSSTransition in={showFront} timeout={300} classNames="flip">
<Card
title={title}
value={key}
onClick={() => {
setShowFront((v) => !v);
setColor("#A8A8A8");
setShowFront(false);
// getLocalData();
}}
/>
</CSSTransition>
</div>
</div>
);
}
You might want to look into using localStorage.setItem and localStorage.getItem. This will store the data in the browser indefinitely using cache. If you want the data to be deleted or in other words, refreshed, you can use session storage. This retains data until that particular tab is closed.

React JS - How to add value from state to specific element

Hey guys, i am working on a germany game called Kniffel. Its basically dice game.
So I am currently saving final score from thrown dice as State.
And then i would like to take that score from state and when i click a button it will save the score to specific element.
You can imagine it more when you check the code.
/* SAVING A SCORE TO STATE */
const [totalValue, setTotalValue] = React.useState(0)
/* HOW I GET IT */
let total = 0
React.useEffect(() => {
dice.map(die => {
if (die.isHeld) {
total += die.value
}
})
setTotalValue(total)
}, [dice])
And i would like every time i click a button, the score from totalValue will pass to P element, where the button is located. Then a game is going to restart and next round you pass the score to different element.
There will me multiple score--inside divs, so i am thinking how should i approach this. Any help will be GOD!
if you guys have any ideas, let me know please.
UPDATE
I little bit change a code and made a another state with multiple scores:
// TOTAL
const [totalValue, setTotalValue] = React.useState()
// TOTALS
const [totalValues, setTotalValues] = React.useState({
ER1: 0,
ER2: 0,
ER3: 0,
ER4: 0,
ER5: 0,
ER6: 0,
Dreier: 0,
Vierer: 0,
Full: 0,
Kleine: 0,
Grobe: 0,
Kniffel: 0,
Chance: 0
})
Then i made a multiple functions, that will update the array.
function er1() {
setTotalValues(prevState => {
return {
...prevState,
ER1: totalValue
}
})
}
function er2() {
setTotalValues(prevState => {
return {
...prevState,
ER2: totalValue
}
})
}
function er3() {
setTotalValues(prevState => {
return {
...prevState,
ER3: totalValue
}
})
}
.......etc
passing functions as props and passing them to buttons:
export default function Score({Score, Next, Values, er1, er2, er3}) {
return (
<div className="score--box">
<div className="score--inside">
<h3>1ER:</h3>
<p>{Values.ER1}</p>
<button onClick={() => {er1();Next()}}>Add score</button>
</div>
<div className="score--inside">
<h3>2ER:</h3>
<p>{Values.ER2}</p>
<button onClick={() => {er2();Next()}}>Add score</button>
</div>
<div className="score--inside">
<h3>3ER:</h3>
<p>{Values.ER3}</p>
<button onClick={() => {er3();Next()}}>Add score</button>
</div>
</div>
)
}
When i look up to this, it will work but its not efficient how i would like it. Any idea how to simplify this?
You can have a state like passTo inside the score component. Then add a button click event listener that identifies the button clicked. You can selectively display value of Score inside correct <p> with condition
// import useState
export default function Score({Score}) {
const [passTo, setPassTo] = useState()
const handleClick = (btnId) => {
setPassTo(btnId);
}
return (
<div className="score--box">
<div className="score--inside">
<h3>1ER:</h3>
<p>{passTo==='1ER' && Score}</p>
<button onClick={() => handleClick('1ER')}>Add score</button>
</div>
<div className="score--inside">
<h3>2ER:</h3>
<p>{passTo==='2ER' && Score}</p>
<button onClick={() => handleClick('2ER')}>Add score</button>
</div>
<div className="score--inside">
<h3>3ER:</h3>
<p>{passTo==='3ER' && Score}</p>
<button onClick={() => handleClick('3ER')}>Add score</button>
</div>
</div>
)
}
To further simplify, if there would be multiple scores like '1ER', '2ER' etc, , you can put those in an array and map through that
// import useState
export default function Score({Score}) {
const scoreArr = ['1ER', '2ER', '3ER'] //As many needed, can pass as props too.
const [passTo, setPassTo] = useState()
const handleClick = (btnId) => {
setPassTo(btnId);
}
return (
<div className="score--box">
<div className="score--box">
{scoreArr.map((score) => {
return (
<div className="score--inside">
<h3>`${score}:`</h3>
<p>{passTo===score && Score}
</p>
<button onClick={() => handleClick(score)}>Add score</button>
</div>);
})}
</div>
</div>
)
}
Lemme know if it helps :)
you can make a ScoreContainder component which contains the button and the <p></p> element
export default function Score({id, score, setSelectedId, selectedId}){
const onClick = ()=>{
setSelectedId(id)
}
return <div className="score--inside">
<h3>1ER:</h3>
<p>{id===selectedId && score}</p>
<button onClick={onClick} name='1ER'>Add score</button>
</div>
}
and in the parent component return this
export const Parent(){
const [totalValue, setTotalValue] = React.useState(0)
const scoreRef = React.useRef(0)
const [selectedId, setSelectedId] = React.useState()
const getRandomnumber = ()=>{ // this causes rerender
scoreRef.current = Math.random() //or any other random funtion
setTotalValue(scoreRef.current)
}
const reset = ()=>{scoreRef.current = 0} //this won't cause rerender and reset the score value without showing the changes on the screen
return <div className="score--box">
<Score id={0} score={totoalValue} selectedId={selectedId} setSelectedId = {setSelectedId}/>
<Score id={1} score={totoalValue} selectedId={selectedId} setSelectedId = {setSelectedId}/>
<Score id={2} score={totoalValue} selectedId={selectedId} setSelectedId = {setSelectedId}/>
</div>
}
If you can keep multi numbers it should better keep in an object instead number
const [totalValues, setTotalValue] = React.useState({})
and only one method can handle all objects
onClick=({target})=>{
totalValues[target.name]=target.value;
}
it just needs your element name to be an identity
return (
<div className="score--box">
<div className="score--inside">
<h3>1ER:</h3>
<p></p>
<button onClick={onClick} name='1ER'>Add score</button>
</div>
<div className="score--inside">
<h3>2ER:</h3>
<p></p>
<button onClick={onClick} name='2ER'>Add score</button>
</div>
<div className="score--inside">
<h3>3ER:</h3>
<p></p>
<button onClick={onClick} name='3ER'>Add score</button>
</div>
</div>
)
Finally you have this object {1ER:4,2ER:5,3ER:2,...}
this a sample
export const Score = () => {
const [totalValues, setTotalValue] = useState({})
const onClick = ({ target }) => {
let randomScore = Math.floor(Math.random() * 6) + 1;
totalValues[target.name] = randomScore;
setTotalValue({...totalValues});
}
return (
<div >
<Label>{totalValues['2ER']}</Label>
<div className="score--inside">
<h3>1ER:</h3>
<p>{totalValues.ER1}</p>
<button onClick={onClick} name='ER1'>Add score</button>
</div>
<div className="score--inside">
<h3>2ER:</h3>
<p>{totalValues['ER2']}</p>
<button onClick={onClick} name='ER2'>Add score</button>
</div>
<div className="score--inside">
<h3>3ER:</h3>
<p>{totalValues['ER3']}</p>
<button onClick={onClick} name='ER3'>Add score</button>
</div>
</div>
);
};

How to toggle item by id or index ? React.js

I need to opet child component by clicked item. FIrst check code:
<div className="d-flex">
{boardList.map((list) => (
<div className="card m-3 p-3" key={list.id}>
<div className="d-flex flex-column">
<h6> {list.name} </h6>
<ul className="list-group">
{list.cards.map((card) => (
<li className="list-group-item" key={card.id}>
{card.name}
</li>
))}
</ul>
{isVisible ? (
<TodoForm onCloseForm={onCloseForm} />
) : (
<small
className="mt-2"
onClick={showInput}
>
Add new task +
</small>
)}
</div>
</div>
))}
</div>
This is work but when I click on 'Add new task +' a child component opens up to me everywhere. i want only the component with the selected id or index to open.
also component for this :
const [isVisible, setIsVisible] = useState(false);
const [boardList, setBoardList] = useState([]);
useEffect(() => {
axiosInstance
.get("")
.then((res) => {
setBoardList(res.data);
console.log("resp", boardList);
})
.catch((err) => {
console.log(err);
});
}, []);
const showInput = () => {
setIsVisible(true);
};
const onCloseForm = () => {
setIsVisible(false);
};
All the items of the resultant array from boardList.map are depending on the same state isVisible, that's why when you click on one of them all the items mimic the same behaviour.
What you need is to create a component with its own state to encapsulate this part of your code
{isVisible ? (
<TodoForm onCloseForm={onCloseForm} />
) : (
<small
className="mt-2"
onClick={showInput}
>
Add new task +
</small>
)}
This way every instance of this new component would have its own isVisible so they no longer would affect their siblings state.
The component would look like this.
const NewComponent = () => {
const [isVisible, setIsVisible] = useState(false);
return <>
{isVisible ? (
<TodoForm onCloseForm={onCloseForm} />
) : (
<small className="mt-2" onClick={() => setIsVisible(true)}>
Add new task +
</small>
)}
</>
};

How to only target a single item in a UI rendered list in React

I am building a simple todo-esk feature where if a user clicks the edit icon, only that item is editable. I implement this currently with a useState hook, const [editingMemberName, setEditingMemberName] = useState(false), but when I call a function, editMemberName all instances of items show an input field. This is not the experience I am going for.
Here are some screen shots that should make this more clear:
As you can see, I have two cards, but when I click the tool icon, both input boxes are displayed.
Here is the code:
const [editingMemberName, setEditingMemberName] = useState(false)
const [memberName, setMemberName] = useState('')
const handleChangeName = (e) => {
setMemberName(e.target.value)
}
// Update member name
const editMemberName = async (e) => {
setEditingMemberName(true)
}
const memberItems = members.map((member) => {
return (
<div
key={member.id}
>
<div className="flex items-center gap-4 w-full">
{editingMemberName ? (
<input
type="text"
placeholder="Johnny Appleseed"
onChange={handleChangeName}
/>
) : (
<>
<div>
{member.name}
</div>
<h3>{member.name}</h3>
</>
)}
</div>
<div>
{editingMemberName ? (
<button
onClick={() => updateMemberName(member.id)}
>
<CgCheckO size=".75em" />
</button>
) : (
<button
onClick={() => editMemberName(member.id)}
>
<FiTool size=".75em" />
</button>
)}
</div>
</div>
)
I've realized that editingMemberName hook operates on all instances, but I am not sure how to only target a single item.
Note: you can assume that the members array has a unique id for each item.
members: [
{
name: "Johnny",
id: 123
},
{
name: "George",
id: 456
}
]
That's because you are referring the boolean to all the boxes and not an individual element, use
const [editingMemberName, setEditingMemberName] = useState(members.map(e => false))
Something along the lines
const editMemberName = async (memberID, index) => {
let new_editing_members_state = members.map(e => false)
new_editing_members_state[index] = true
setEditingMemberName(new_editing_members_state)
}
const memberItems = members.map((member, index) => {
return (
<div
key={member.id}
>
<div className="flex items-center gap-4 w-full">
{editingMemberName ? (
<input
type="text"
placeholder="Johnny Appleseed"
onChange={handleChangeName}
/>
) : (
<>
<div>
{member.name}
</div>
<h3>{member.name}</h3>
</>
)}
</div>
<div>
{editingMemberName[index] ? (
<button
onClick={() => updateMemberName(member.id)}
>
<CgCheckO size=".75em" />
</button>
) : (
<button
onClick={() => editMemberName(member.id,index)}
>
<FiTool size=".75em" />
</button>
)}
</div>
</div>
)

How to add the product to the favorites?

I am currently making a project over the database I created using Mock API. I created a button, created addToFavorites function. When the button was clicked, I wanted the selected product's information to go to the favorites, but I couldn't. I would be glad if you could help me on how to do this.
(Favorites.js empty now. I got angry and deleted all the codes because I couldn't.)
(
Recipes.js
import React, { useState, useEffect } from "react"
import axios from "axios"
import "./_recipe.scss"
import Card from "../Card"
function Recipes() {
const [recipes, setRecipes] = useState([])
const [favorites, setFavorites] = useState([])
useEffect(() => {
axios
.get("https://5fccb170603c0c0016487102.mockapi.io/api/recipes")
.then((res) => {
setRecipes(res.data)
})
.catch((err) => {
console.log(err)
})
}, [])
const addToFavorites = (recipes) => {
setFavorites([...favorites, recipes])
console.log("its work?")
}
return (
<div className="recipe">
<Card recipes={recipes} addToFavorites={addToFavorites} />
</div>
)
}
export default Recipes
Card.js
import React, { useState } from "react"
import { Link } from "react-router-dom"
import { BsClock, BsBook, BsPerson } from "react-icons/bs"
function Card({ recipes, addToFavorites }) {
const [searchTerm, setSearchTerm] = useState("")
return (
<>
<div className="recipe__search">
<input
type="text"
onChange={(event) => {
setSearchTerm(event.target.value)
}}
/>
</div>
<div className="recipe__list">
{recipes
.filter((recipes) => {
if (searchTerm === "") {
return recipes
} else if (
recipes.title.toLowerCase().includes(searchTerm.toLowerCase())
) {
return recipes
}
})
.map((recipe) => {
return (
<div key={recipe.id} className="recipe__card">
<img src={recipe.image} alt="foods" width={350} height={230} />
<h1 className="recipe__card__title">{recipe.title}</h1>
<h3 className="recipe__card__info">
<p className="recipe__card__info--icon">
<BsClock /> {recipe.time} <BsBook />{" "}
{recipe.ingredientsCount} <BsPerson />
{recipe.servings}
</p>
</h3>
<h3 className="recipe__card__desc">
{recipe.description.length < 100
? `${recipe.description}`
: `${recipe.description.substring(0, 120)}...`}
</h3>
<button type="button" className="recipe__card__cta">
<Link
to={{
pathname: `/recipes/${recipe.id}`,
state: { recipe }
}}
>
View Recipes
</Link>
</button>
<button onClick={() => addToFavorites(recipes)}>
Add to favorites
</button>
</div>
)
})}
</div>
</>
)
}
export default Card
Final Output:
I have implemented the addToFavorite() and removeFavorite() functionality, you can reuse it the way you want.
I have to do bit of modification to the code to demonstrate its working, but underlying functionality of addToFavorite() and removeFavotie() works exactly the way it should:
Here is the Card.js where these both functions are implemented:
import React, { useState } from "react";
import { BsClock, BsBook, BsPerson } from "react-icons/bs";
function Card({ recipes }) {
const [searchTerm, setSearchTerm] = useState("");
const [favorite, setFavorite] = useState([]); // <= this state holds the id's of all favorite reciepies
// following function handles the operation of adding fav recipes's id's
const addToFavorite = id => {
if (!favorite.includes(id)) setFavorite(favorite.concat(id));
console.log(id);
};
// this one does the exact opposite, it removes the favorite recipe id's
const removeFavorite = id => {
let index = favorite.indexOf(id);
console.log(index);
let temp = [...favorite.slice(0, index), ...favorite.slice(index + 1)];
setFavorite(temp);
};
// this variable holds the list of favorite recipes, we will use it to render all the fav ecipes
let findfavorite = recipes.filter(recipe => favorite.includes(recipe.id));
// filtered list of recipes
let filtered = recipes.filter(recipe => {
if (searchTerm === "") {
return recipe;
} else if (recipe.title.toLowerCase().includes(searchTerm.toLowerCase())) {
return recipe;
}
});
return (
<div className="main">
<div className="recipe__search">
<input
type="text"
onChange={event => {
setSearchTerm(event.target.value);
}}
/>
</div>
<div className="recipe-container">
<div className="recipe__list">
<h2>all recipes</h2>
{filtered.map(recipe => {
return (
<div key={recipe.id} className="recipe__card">
<img src={recipe.image} alt="foods" width={50} height={50} />
<h2 className="recipe__card__title">{recipe.title}</h2>
<h4 className="recipe__card__info">
<p>
<BsClock /> {recipe.time} <BsBook />{" "}
{recipe.ingredientsCount} <BsPerson />
{recipe.servings}
</p>
</h4>
<h4 className="recipe__card__desc">
{recipe.description.length < 100
? `${recipe.description}`
: `${recipe.description.substring(0, 120)}...`}
</h4>
<button onClick={() => addToFavorite(recipe.id)}>
add to favorite
</button>
</div>
);
})}
</div>
<div className="favorite__list">
<h2>favorite recipes</h2>
{findfavorite.map(recipe => {
return (
<div key={recipe.id} className="recipe__card">
<img src={recipe.image} alt="foods" width={50} height={50} />
<h2 className="recipe__card__title">{recipe.title}</h2>
<h4 className="recipe__card__info">
<p className="recipe__card__info--icon">
<BsClock /> {recipe.time} <BsBook />{" "}
{recipe.ingredientsCount} <BsPerson />
{recipe.servings}
</p>
</h4>
<h4 className="recipe__card__desc">
{recipe.description.length < 100
? `${recipe.description}`
: `${recipe.description.substring(0, 120)}...`}
</h4>
<button onClick={() => removeFavorite(recipe.id)}>
remove favorite
</button>
</div>
);
})}
</div>
</div>
</div>
);
}
export default Card;
Here is the live working app : stackblitz
You can get the previous favourites recipes and add the new ones.
const addToFavorites = (recipes) => {
setFavorites(prevFavourites => [...prevFavourites, recipes])
console.log("its work?")
}

Categories