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

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>
);
};

Related

Cant remove specific React components

This is my Add_Blog.js file
import React, { useState } from "react";
function AddBlogs() {
const ADD = () => {
return (
<div>
<label>Topic</label>
<input></input>
<label>Content</label>
<textarea></textarea>
<button type="button" onClick={deleteContent} value={state.length}> Delete</button>
</div>
);
}
const [state, setState] = useState([<ADD key="first"></ADD>]);
const addblog = () => {
setState([...state, <ADD key={state.length}></ADD>])
}
const deleteContent = (event, key) => {
setState([...state, state.splice(event.target.value, 1)]);
}
return (
<div>
<form>
<div>
<label>Add Title</label>
<input></input>
</div>
<div>
{
state
}
</div>
<div>
<button type="button" onClick={addblog}>Add another topic</button>
</div>
<input type="submit" value="publish"></input>
</form>
</div>
);
}
export default AddBlogs;
My expectation from this code is to remove component left of delete button when I press that delete button. But when I press delete button it removes that component and every components below it.
Any solution for this?
It does not work because you are using the splice method wrong. It returns the deleted value.
You can change your deleteContent function like this:
const deleteContent = (event, key) => {
let indexToRemove = Number(event.target.value);
setState((prevState) => prevState.filter((elem, index) => index !== indexToRemove));
}
That will be enough for the program to work as expected

Why is this React function returning NaN?

I am making a very simple calculator in React, and I am very new to JS and React js.
So, this code just tries to add a few numbers and returns them in H1.
import React, { Component } from "react";
class App extends React.Component {
state = {
num1: 0,
num2: 0,
answer: 5,
};
count = (number) => {
this.setState({ num1: number });
return number;
};
count2 = (number2) => {
this.setState({ num2: number2 });
return number2;
};
count3 = () => {
this.setState({ answer: this.count() + this.count2() });
console.log(this.state.answer);
console.log(this.count2());
return this.state.answer;
};
render() {
return (
<div>
<div>
<h1>{this.state.num1}</h1>
<button onClick={() => this.count(1)}> 1 </button>
<button onClick={() => this.count(2)}> 2 </button>
<button onClick={() => this.count(3)}> 3 </button>
<button onClick={() => this.count(4)}> 4 </button>
<button onClick={() => this.count(5)}> 5 </button>
<button onClick={() => this.count(6)}> 6 </button>
<button onClick={() => this.count(7)}> 7 </button>
</div>
<div>
<h1>{this.state.num2}</h1>
<button onClick={() => this.count2(1)}> 1 </button>
<button onClick={() => this.count2(2)}> 2 </button>
<button onClick={() => this.count2(3)}> 3 </button>
<button onClick={() => this.count2(4)}> 4 </button>
<button onClick={() => this.count2(5)}> 5 </button>
<button onClick={() => this.count2(6)}> 6 </button>
<button onClick={() => this.count2(7)}> 7 </button>
</div>
<div>
<button onClick={() => this.count3()}>click</button>
<h1>{this.state.answer}</h1>
</div>
</div>
);
}
}
export default App;
So, currently the answer is returning NaN after clicking it. Why is it happening so, and how to fix it?
Your count methods expect a number passed as an argument but you're not supplying them so state gets updated with undefined in each case, so your answer is NaN.
Have each method simply update the state (no need to return anything from them) and then have count3 update the state with those two numbers.
const { Component } = React;
class App extends React.Component {
state = { num1: 0, num2: 0, answer: 5 };
count = (number) => {
this.setState({ num1: number });
};
count2 = (number2) => {
this.setState({ num2: number2 });
};
count3 = () => {
const { num1, num2 } = this.state;
this.setState({ answer: num1 + num2 });
};
render() {
return (
<div>
<div>
<h1>{this.state.num1}</h1>
<button onClick={() => this.count(1)}> 1 </button>
<button onClick={() => this.count(2)}> 2 </button>
<button onClick={() => this.count(3)}> 3 </button>
<button onClick={() => this.count(4)}> 4 </button>
<button onClick={() => this.count(5)}> 5 </button>
<button onClick={() => this.count(6)}> 6 </button>
<button onClick={() => this.count(7)}> 7 </button>
</div>
<div>
<h1>{this.state.num2}</h1>
<button onClick={() => this.count2(1)}> 1 </button>
<button onClick={() => this.count2(2)}> 2 </button>
<button onClick={() => this.count2(3)}> 3 </button>
<button onClick={() => this.count2(4)}> 4 </button>
<button onClick={() => this.count2(5)}> 5 </button>
<button onClick={() => this.count2(6)}> 6 </button>
<button onClick={() => this.count2(7)}> 7 </button>
</div>
<div>
<button onClick={() => this.count3()}>click</button>
<h1>{this.state.answer}</h1>
</div>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
There is a slightly more concise version of your code that you may find useful. It involves using a loop to add the buttons, and attaching only one listener to the parent div. The listener will catch the button events as they bubble up the DOM, and you can use data attributes from the parent and the button to update the specific state property.
(The even more "React Way" would be identify those parts of your JSX that repeat, and maybe to create reusable Number and NumberGroup components; but that's code for another day.)
const { Component } = React;
class App extends React.Component {
state = { num1: 0, num2: 0, answer: 0 };
// Take the group id from the parent dataset,
// and the id from the button that was clicked,
// and use that information to update the state
// Note: in this example, because we're using a
// data attribute (a string) we need to coerce it
// to a number
handleClick = (e) => {
const { group } = e.target.parentNode.dataset;
const { id } = e.target.dataset;
this.setState({ [group]: Number(id) })
}
// Return an array of seven buttons each
// with their own id
getButtons = () => {
const buttons = [];
for (let i = 1; i <= 7; i++) {
buttons.push(<button data-id={i}>{i}</button>);
}
return buttons;
}
// When the "Update total" button is clicked
// get the numbers from state and update the total
updateTotal = () => {
const { num1, num2 } = this.state;
this.setState({ answer: num1 + num2 })
}
render() {
return (
<div>
<div data-group="num1" onClick={this.handleClick}>
<h3>{this.state.num1}</h3>
{this.getButtons()}
</div>
<div data-group="num2" onClick={this.handleClick}>
<h3>{this.state.num2}</h3>
{this.getButtons()}
</div>
<div class="answer">
<button onClick={this.updateTotal}>Update total</button>
<h3>Total: {this.state.answer}</h3>
</div>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('react')
);
.answer { margin-top: 1em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
maybe you can try change your count3 method like this:
count3 = () => {
this.setState({ answer: this.state.num1 + this.state.num2 });
return this.state.answer;
};
then when you click button trigger count3 function to setState answer based on your state.num1 and state.num2 and the answer will be change.

implementing local storage in react application

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.

Is it possible to display updated state from useState in another component?

I have this component which uses useState const [answers, setAnswerFunction] = useState(options);
Once the answers state has been updated in this component I would like to use the updated state and display it in another component. Is this possible?
I had a look at a similar question which says to use useContext I have not looked into this yet as I have never used it (Is it possible to share states between components using the useState() hook in React?) but I wondered if there would be a simpler way?
Code:
const QuestionBox = ({
question,
options,
correct,
incrementScore,
incrementResponse,
}) => {
const [response, setResponse] = useState("");
const [answers, setAnswerFunction] = useState(options);
const computeAnswer = answer => {
if (answer === correct) {
setResponse("correct");
incrementScore();
incrementResponse();
} else {
setResponse("sorry wrong!");
incrementResponse();
}
};
return (
<div className="questionBox">
<div className="question"> {question} </div>
{answers.map((answer, index) => {
return (
<button
key={index}
className="answerBtn"
type="button"
onClick={() => {
setAnswerFunction([answer]);
computeAnswer(answer);
}}
>
{answer}
</button>
);
})}
{response === "correct" ? (
<div className="correctResponse"> {response} </div>
) : (
<div className="wrongResponse"> {response} </div>
)}
</div>
);
};
export default QuestionBox;
I want to display the state from the component abover answers here on Result card via the prop userAnswer:
const ResultCard = ({
score,
getQuestions,
qbank,
userAnswer
}) => {
return (
<div>
<div>You scored {score} out of 5! </div>
<div className="playBtnBox">
<button className="playBtn" type="button" onClick={getQuestions}>
Play again
</button>
</div>
<div>
{qbank.map((questionObject) => {
return (
<div>
<div className="questionBox"> {questionObject.question}</div>
<div className="resultCardCorrect"> Correct Answer: {questionObject.correct}</div>
</div>
);
})}
</div>
<div className="resultCardCorrect"> Your Answer: {userAnswer}</div>
</div>
);
};
export default ResultCard;

How could i pass a value from a child component to parent, when i have multiple child components?

I creat multiple components, they have the same hierarchy, and inside they i also call other component call , now i want to create functions on my which will update the values i'm passing as props to my component. I manage to pass the functions as props, but can't manage to pass the value from child as parameters to the functions, so i could update only the props especific to that child.
App
function App() {
// eslint-disable-next-line
const [content, setContent] = useState(images)
const [count, setCount] = useState(content.votes)
console.log(count)
const upVote = (id) => {
alert('up =>', id)
}
const downVote = () => {
alert('down')
}
return (
<div className="App">
<div className="grid">
{content.map((picture, index) => {
return <Card key={index} picture={picture} teste={[upVote, downVote]}/>
})
}
</div>
</div>
)
}
Card
function Card({ picture, teste }) {
return (
<div className="card">
<div className="container">
<img
width="300"
alt={`id: ${picture.id}`}
src={picture.src}
className="image"
/>
<Options votes={0} originalPost={picture.original_post} teste={teste[0]}/>
</div>
</div>
)
}
Options
function Options({ votes, originalPost, teste }) {
const [count, setCount] = useState(votes)
const [styling, setStyling] = useState('#696969')
function countStyle(count) {
if (count > 0){
setStyling('#008000')
} else if (count < 0) {
setStyling('#B22222')
} else {
setStyling('#696969')
}
}
return (
<div>
<button onClick={() => teste(count)} className="buttons">teste</button>
<button title="Down vote" onClick={() => {
setCount(count - 1)
countStyle(count-1)
// style(count - 1)
}} className="buttons">-</button>
<span title="Vote counter" style={{color: styling}} className="counter">{count}</span>
<button title="Up vote" onClick={() => {
setCount(count + 1)
// style(count + 1)
countStyle(count +1)
}} className="buttons">+</button><br></br>
<a href={originalPost}
target="_blank"
title="Click to check the original post"
rel="noopener noreferrer"
className="link">Original post</a>
</div>
)
}
I would start by consolidating your state into the App component. Save the votes on your content array on each picture object. Pass the upvote and downvote functions down to each children and call them from your button clicks. I would also calculate the styling based on the props, rather than use state.
App
function App() {
let initialstate = images.map(image => {
image.votes = 0;
return image;
});
const [content, setContent] = useState(initialstate);
const upVote = index => {
setContent(content[index].votes + 1);
};
const downVote = index => {
setContent(content[index].votes - 1);
};
return (
<div className="App">
<div className="grid">
{content.map((picture, index) => {
return <Card key={index} picture={picture} index={index} upVote={upVote} downVote={downVote} />;
})}
</div>
</div>
);
}
Card
function Card({ index, picture, ...props }) {
const upVote = () => props.upVote(index);
const downVote = () => props.downVote(index);
return (
<div className="card">
<div className="container">
<img
width="300"
alt={`id: ${picture.id}`}
src={picture.src}
className="image"
/>
<Options votes={picture.votes} originalPost={picture.original_post} upVote={upVote} downVote={downVote}/>
</div>
</div>
)
Options
function Options({ votes, originalPost, upVote, downVote }) {
let styling = '#696969';
if (count > 0) {
styling = '#008000';
} else if (count < 0) {
styling = '#B22222';
} else {
styling = '#696969';
}
return (
<div>
<button title="Down vote" onClick={downVote} className="buttons">
-
</button>
<span title="Vote counter" style={{ color: styling }} className="counter">
{votes}
</span>
<button title="Up vote" onClick={upVote} className="buttons">
+
</button>
<br></br>
<a
href={originalPost}
target="_blank"
title="Click to check the original post"
rel="noopener noreferrer"
className="link"
>
Original post
</a>
</div>
);
}

Categories