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.
Related
i have a button component from material UI. What i wanna do is i click a button and it renders to me a new Button on the screen.
That's what i did
const [button, setButton] = useState([]);
function addButton(){
let newObj = {
button: <Button variant="contained"> A Button</Button>,
};
setButton([...button, newObj])
}
And then in the main function return i did
{button.length !== 0 ? (
button.map((item) => {
<div>{item.button}</div>;
})
) : (
<div>Hello</div>
)}
What am i doing wrong?
When you use a multi-line lambda with braces you need a return...
button.map((item) => {
return (<div>{item.button}</div>);
})
Alternatively you can omit the braces...
button.map((item) => (<div>{item.button}</div>))
This answer tries to address the below problem-statement:
But how could i click one button and render as many buttons as i
clicked?
The below snippet provides a demo of how a new button may be rendered when user clicks an existing button. This expands on the below comment
Creating a new button on clicking an existing button may be
accomplished without needing to hold the component in state.
Please note:
It does not store the button elements into the state
instead, it merely stores the attributes (like the button-label /
text) into the state
Code Snippet
const {useState} = React;
const MyButton = ({
btnLabel, btnName, handleClick, isDisabled, ...props
}) => (
<button
class="myBtn"
onClick={handleClick}
name={btnName}
disabled={isDisabled}
>
{btnLabel}
</button>
);
const MagicButtons = () => {
const [btnText, setBtnText] = useState("");
const [disableBtns, setDisableBtns] = useState(false);
const [myButtons, setMyButtons] = useState(["A Button"]);
const handleClick = () => setDisableBtns(true);
return (
<div>
{
disableBtns && (
<div class="myInput">
<input
value={btnText}
onChange={e => setBtnText(e.target.value)}
/>
<button
onClick={() => {
setMyButtons(prev => ([...prev, btnText]));
setBtnText("");
setDisableBtns(false);
}}
>
Add New Button
</button>
</div>
)
}
{
myButtons.map((txt, idx) => (
<MyButton
handleClick={handleClick}
btnName={idx}
isDisabled={disableBtns ? "disabled" : ""}
btnLabel={txt}
/>
))
}
</div>
);
};
ReactDOM.render(
<div>
DEMO
<MagicButtons />
</div>,
document.getElementById("rd")
);
.myBtn { margin: 15px; }
.myInput > input { margin: 15px; }
<div id="rd" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
You forgot to return the component in the map function
like this
{button.length !== 0 ? (
button.map((item) => {
return (<div>{item.button}</div>);
})
) : (
<div>Hello</div>
)}
the map function with no 'return' keyword must not have the bracket { }
like this
button.map((item) => (<div>{item.button}</div>))
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>
);
};
I have this jsx expression that renders a maximum of 5 items from an array using the map function. How can I make it so that the rest of the items render on click of the button? The list of items in the array is unknown as it's coming from an API, so sometimes it can be 10 items or 15 items, etc.
const Second = ({container}) => {
return(
<div>
{container?.slice(0, 5).map((container) => (
<h3>{container}</h3>
))}
<button type="button" class="btn btn-primary">Primary</button>
</div>
)
}
You can have a state that determines that and then set the state to true on click of the button: Someting like this
const Second = ({container}) => {
const [showMore, setShowMore] = useState(false);
return(
<div>
{container?.slice(0, 5).map((container) => (
<h3>{container}</h3>
))}
{showMore && container?.slice(5).map((container) => (
<h3>{container}</h3>
))} //this would show the remaining values
<button type="button" class="btn btn-primary" onClick={() => setShowMore(true)}>Primary</button>
</div>
)
}
you can declare a boolean state variable to determine if you want to show all the items or only the first five, this variable will be updated when you click on the button :
function Second({container = []}) {
const [showAll, setShowAll] = useState(false);
function handleClick() {
setShowAll(prevShowAll => !prevShowAll);
}
const items = showAll ? container : container.slice(0, 5);
return(
<div>
{items.map(item => <h3>{item}</h3>)}
<button type="button" class="btn btn-primary" onClick={handleClick}>
{showAll ? "Show first five items" : "Show all items"}
</button>
</div>
)
}
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.
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>
);
}