How can I stop the React buttons from rendering synchronously? - javascript

Every time I click the "increase" or "decrease" buttons for a certain player, I want the score to increase for that player only. However, right now when I click either of the buttons it increases both scores. I know it is probably something simple that I am completely overlooking, but any help is appreciated. Thank you in advance!
Here is my code https://codesandbox.io/s/objective-curran-6ogn3?file=/src/App.js:0-2090:
import "./styles.css";
import {Component, useState} from 'react';
export default function App() {
const [count, setCount] = useState(0);
const [playerScore, setplayerScore] = useState([{
nameOne: 'Player 1'
}, {
nameTwo: 'Player 2'
}
]);
const increase = () => {
setCount(count + 1);
setplayerScore(playerScore);
};
const decrease = () => {
setCount(count - 1);
setplayerScore(playerScore);
};
return (
<main>
{/* player 1*/}
<div>
{playerScore.map((player) => {
return (
<div>
<h2 className="name">{player.nameOne}</h2>
</div>
);
})}
</div>
<div>
<div className="score">{count}</div>
{/* increase button */}
<button onClick={increase}>
Increase
</button>
{/* decrease button */}
<button onClick={decrease}>
Decrease
</button>
</div>
{/* player 2 */}
<div>
{playerScore.map((player) => {
return (
<div>
<h2 className="name">{player.nameTwo}</h2>
</div>
);
})}
</div>
<div>
<div className="score">{count}</div>
<section>
<button onClick={increase}>Increase</button>
<button onClick={decrease}>Decrease</button>
</section>
</div>
</main>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

I did this fork based on your code. https://codesandbox.io/s/cranky-hooks-q9ntu?file=/src/App.js
I have refactored some logic and added the functionality that I understand you want. Specially the increase/decrease logic.
The main error was that you were using same state for the entire array, so the value will always be the same for all the items.

Related

Hiding and showing content on click using React

In a nutshell, i am creating a case study for a potential job, the employer wants me to use a React app to create it...
I want to create a button that has the start function that:
Hides original content
displays the hidden content,
i got the hidden content to show but the original content is still visible, any help?
my code below:
import React, { useState } from 'react'
function Body() {
const [show, setShow] = useState(true);
const [hide, setHidden] = useState(true);
return (
<>
<div className='container'>
<div className="start-container">
<h2>Click Start To View The Application</h2>
<button onClick={ () => setShow(s => ! s) } className='btn'>Start!</button>
</div>
{/* URL Link Input */}
<div>
{!show ? <form action="GET">
<input type="text" />
<button className='btn'>Submit</button>
</form> : null }
</div>
</div>
</>
)
}
export default Body
You are close, you need to have the original content in the ternary so it's hidden once you toggle show. I also changed setShow to set show to false rather than the previous value since it doesn't matter because the button is not toggable because once you click it and can't re toggle the original content.
import React, { useState } from 'react'
function Body() {
const [show, setShow] = useState(true);
return (
<div className='container'>
{show ? (
<div className="start-container">
<h2>Click Start To View The Application</h2>
<button onClick={() => setShow(false)} className='btn'>Start!</button>
</div>
) : (
<form action="GET">
<input type="text" />
<button className='btn'>Submit</button>
</form>
)
</div>
)
}
export default Body
it dose not need hide state and you can toggle visibility just with show state. try this:
{ show ? <form action="GET">
<input type="text" />
<button className='btn'>Submit</button>
</form> : null
}
This should work.
import React, { useState } from 'react';
function Body() {
const [show, setShow] = useState(false);
return (
<>
<div className="container">
{/* URL Link Input */}
<div>
{show ? (
<form action="GET">
<input type="text" />
<button className="btn">Submit</button>
</form>
) : (
<div className="start-container">
<h2>Click Start To View The Application</h2>
<button onClick={() => setShow(!show)} className="btn">
Start!
</button>
</div>
)}
</div>
</div>
</>
);
}
export default Body;
You could use an appStarted state and then render your component conditionnally:
const { useState } = React
function Body() {
const [appStarted, setAppStarted] = useState(false)
return (
<div className="container">
{appStarted ? (
<div>
<h2>Hidden Content</h2>
</div>
) : (
<div className="start-container">
<h2>Click Start To View The Application</h2>
<button onClick={ () => setAppStarted(true) } className='btn'>Start!</button>
</div>
)}
</div>
)
}
ReactDOM.render(<Body />, document.getElementById("root"))
<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="root"></div>

How do i change the color of a button when clicked in react? [duplicate]

This question already has an answer here:
How to change background color of button onClick in react js?
(1 answer)
Closed 1 year ago.
I am new to react and i am trying to use the useState hook to change the color of a button when clicked. Please how do i go about it . Here is the code below
import { Button } from 'bootstrap'
import React, { useState } from 'react'
import "../workout/style.css"
const [buttonColor, setButtonColor] = useState()
function Workout() {
let buttonColor = document.getElementsByClassName("button")
return (
<div>
<div class="container">
<button className="button">BODYWEIGHT</button>
<button className="button">BARBELL</button>
<button className="button">DUMBELLS</button>
<button className="button">KETTLEBELLS</button>
<button className="button">STRETCHES</button>
</div>
</div>
)
}
export default Workout
Here are a few examples of two ways to color button backgrounds onClick:
const { useState } = React;
function App() {
const [btnClass, setBtnClass] = useState(false);
const [btnColor, setBtnColor] = useState("red");
return (
<div className="App">
<button
onClick={() => {
btnClass ? setBtnClass(false) : setBtnClass(true);
}}
className={btnClass ? "btnClass clicked" : "btnClass"}
>
button
</button>
<button
onClick={() => {
btnColor === "red" ? setBtnColor("green") : setBtnColor("red");
}}
style={{ backgroundColor: btnColor }}
>
button
</button>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
.App {
font-family: sans-serif;
text-align: center;
}
.btnClass {
background-color: red;
}
.btnClass.clicked {
background-color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
https://codesandbox.io/s/focused-cloud-m0qqx?file=/src/styles.css:0-148
The states in react are special variables.
They work like constants so when you assign a value, it wont change at all.
And when you set a new value with the setState function, (the only way to change the value btw) the component rerender.
So, to answer your question... Apply an onClick property to a button.
<div>
<div class="container">
<button className="button" onClick={()=> setButtonColor(newColor)} style={{backgroundColor: `${buttonColor}`}}>BODYWEIGHT</button>
<button className="button">BARBELL</button>
<button className="button">DUMBELLS</button>
<button className="button">KETTLEBELLS</button>
<button className="button">STRETCHES</button>
</div>
</div>
Bear in mind that buttonColor the waybi used this code, would take values like 'red', 'blue ' ... ect
Please check useEfect or SetState, i recommend UseEfect for this case.
go to documentation for more information https://reactjs.org/docs/hooks-state.html
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
remember add bem on your component and load this.

using button to increment divs in react

I am fairly new to React/Next and I had a quick question.
I am trying to create a button that will increment the number of divs in real time.
Here is my code:
import React from 'react'
const Clown = () => {
const [clownCounter, setClownCounter] = React.useState(1);
function addClown(event) {
event.preventDefault();
}
return(
<React.Fragment>
<div>
<form>
{Array.from({ length: clownCounter}, (_unused, index) => index + 1).map(
(clownIndex) => {
const clownid = `${clownIndex}`
return (
<div key={clownid } className="clown-box">
<label htmlFor={clownid }>Activity {clownIndex}</label>
<br />
<input type="text" onChange={(e)=> onChangeForm(e)} name={activityId} id={activityId} />
<br />
</div>
)
},
)}
<span className="clown-add">
<button onClick={addClown} onChange={() => { setClownCounter(clownCounter++) }}>Add Clown</button>
</span>
<br />
</form>
</div>
</React.Fragment>
)
}
export default Clown
As you can see the goal is to increase the amount of clown-box divs everytime the button is clicked. I think I am close but it is not currently working. Can anyone help?
There are few small this wrong with your code.
First, you have an extra comma(,) after the return statement in map function
Second, you are updating state clownCounter on onChange event in button, which is incorrect. You should update it on click and also prevent the default behaviour of form submit on click of button or you can define the button type to be type="button"
Lastly, you need to define your onChangeForm function
const Clown = () => {
const [clownCounter, setClownCounter] = React.useState(1);
function onChangeForm() {
}
function addClown(event) {
event.preventDefault();
setClownCounter(prev=> prev+1);
}
console.log(clownCounter);
return(
<div>
<form>
{Array.from({ length: clownCounter}, (_unused, index) => index + 1).map(
(clownIndex) => {
const clownid = `${clownIndex}`;
return (
<div key={clownid } className="clown-box">
<label htmlFor={clownid }>Activity {clownIndex}</label>
<br />
<input type="text" onChange={(e)=> onChangeForm(e)} name={'activityId'} id={'activityId'} />
<br />
</div>
)
})
}
<span className="clown-add">
<button type="button" onClick={addClown}>Add Clown</button>
</span>
<br />
</form>
</div>
)
}
ReactDOM.render(<Clown />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="app" />
Edit: Thought issue was caused by Array.from, but, it's not. I've removed that part, but kept the example since OP might find it useful
const { useState } = React;
const Clowns = ({ title }) => {
const [clownCounter, setClownCounter] = React.useState(1);
return (
<div>
<button onClick={() => setClownCounter(clownCounter + 1)}>
Add clown
</button>
<div className='clowns'>
{Array.from({ length: clownCounter}, (_unused, index) => index + 1).map((e, i) => (
<div>
<h4>{`Clown #${i + 1}`}</h4>
<img src={`https://placehold.it/150x150&text=Clown%20%23${i + 1}`} />
</div>
))}
</div>
</div>
);
};
ReactDOM.render(<Clowns />, document.getElementById("react") );
.clowns { display: flex; flex-direction: column; }
h4 { margin-bottom: 5px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

How to make a component (popup modal) appear only once using React hooks or otherwise (React/Next js)

I want to make a popup modal appear only once per visit (I don't want it to appear every time someone goes back to the index page). Is there a way to do this using hooks or is there a better method?
export default function Popup() {
const [visible, setVisible] = React.useState(true);
if(!visible) return null;
return (
<div className={styles.popup} onClick={() => setVisible(false)}>
{/* <div className={styles.popupInner}> */}
<div className={styles.popupInner}>
<div className={styles.buttonContainer}><Button color="danger" className={styles.button}>Okay</Button></div>
</div>
{/* </div> */}
</div>
)
}
You could do with localstorage and useEffect hook
why localstorage ?
You could achieve same result using useContext hook. But at the time of refresh.Hooks not hold the previous value.so better use localstorage to store the pop status
export default function Popup() {
const [visible, setVisible] = React.useState(false);
useEffect(()=>{
let pop_status = localStorage.getItem('pop_status');
if(!pop_status){
setVisible(true);
localStorage.setItem('pop_status',1);
}
},[])
if(!visible) return null;
return (
<div className={styles.popup} onClick={() => setVisible(false)}>
{/* <div className={styles.popupInner}> */}
<div className={styles.popupInner}>
<div className={styles.buttonContainer}><Button color="danger" className={styles.button}>Okay</Button></div>
</div>
{/* </div> */}
</div>
)
}
export default function Popup() {
const [visible, setVisible] = React.useState(true);
if(!visible) return null;
return (
<div className={styles.popup}>
<button onClick={() => setVisible(false)}>Click</button>
{ visible ? <div className={styles.popupInner}>
<div className={styles.popupInner}>
<div className={styles.buttonContainer}>
<Button color="danger" className={styles.button}>Okay</Button>
</div>
</div>
</div> : "" }
</div>
)
}

Identify Clicked Button In Unordered List: React

I a week new in learning react coming from an angular background. I have the following unordered list in React.
const QueueManage: React.FC = () => {
const { queue, setQueue, loading, error } = useGetQueue();
const [btnState, setBtnState] = useState(state);
const enterIconLoading = (event: React.MouseEvent<HTMLElement, MouseEvent>) => {
const item = '';
const btn = '';
console.log(item, btn);
setBtnState({ loading: true, iconLoading: true, item: item, btnType: btn });
};
<ul className="listCont">
{queue.map(queueItem => (
<li className="col-12" key={queueItem.id}>
<div className="row">
<div className="listName col-3">
<p>{queueItem.user.firstName} {queueItem.user.lastName}</p>
</div>
<div className="listName col-5">
<div className="row">
<div className="col-3">
<Button type="primary" loading={btnState.loading} onClick={enterIconLoading}>
Assign
</Button>
</div>
<div className="col-3">
<Button type="primary" loading={btnState.loading} onClick={enterIconLoading}>
Absent
</Button>
</div>
<div className="col-3">
<Button type="primary" loading={btnState.loading} onClick={enterIconLoading}>
Done
</Button>
</div>
<div className="col-3">
<Button type="primary" loading={btnState.loading} onClick={enterIconLoading}>
Cancel
</Button>
</div>
</div>
</div>
</div>
</li>
)
)}
</ul>
}
For each list item, the list item will have for buttons, namely Assign, Absent, Done, Cancel. My goal is to identify which button was clicked and for which list item so that I can apply a loader for that specific button. Can any one please assist me with an explanation of how I can achieve this in my code
Here is a visual representation of the list that i get
https://i.imgur.com/kxcpxOo.png
At the moment went i click one button, all buttons are applied a spinner like below:
Your assistance and explanation is highly appreciated.
The Reactful approach involved splitting the li into a separate component. This will help keep each item's state separate. Let's call that QueueItem.
const QueueItem = ({ user }) => {
const [loading, setLoading] = useState(false)
function onClickAssign() {
setLoading(true)
// do something
setLoading(false)
}
function onClickAbsent() {
setLoading(true)
// do something
setLoading(false)
}
function onClickDone() {
setLoading(true)
// do something
setLoading(false)
}
function onClickCancel() {
setLoading(true)
// do something
setLoading(false)
}
return (
<li className='col-12'>
<div className='row'>
<div className='listName col-3'>
<p>
{user.firstName} {user.lastName}
</p>
</div>
<div className='listName col-5'>
<div className='row'>
<div className='col-3'>
<Button type='primary' loading={loading} onClick={onClickAssign}>
Assign
</Button>
</div>
<div className='col-3'>
<Button type='primary' loading={loading} onClick={onClickAbsent}>
Absent
</Button>
</div>
<div className='col-3'>
<Button type='primary' loading={loading} onClick={onClickDone}>
Done
</Button>
</div>
<div className='col-3'>
<Button type='primary' loading={loading} onClick={onClickCancel}>
Cancel
</Button>
</div>
</div>
</div>
</div>
</li>
)
}
Here I've also split out each button's onClick into a separate callback since they are well defined and probably have unique behaviours. Another approach mentioned above in a comment is
function onClickButton(action) {
...
}
<Button type='primary' loading={loading} onClick={() => onClickButton('cancel')}>
Cancel
</Button>
This follows the action / reducer pattern which might be applicable here instead of state (useState)
Move the buttons or the whole li to a component and let each list manage it's state.
// Get a hook function
const {useState} = React;
//pass the index of li as prop
const Buttons = ({ listId }) => {
const [clicked, setClickedButton] = useState(0);
return (
<div>
<button
className={clicked === 1 && "Button"}
onClick={() => setClickedButton(1)}
>
Assign
</button>
<button className={clicked === 2 && "Button"} onClick={() => setClickedButton(2)}>Absent</button>
<button className={clicked === 3 && "Button"} onClick={() => setClickedButton(3)}>Done</button>
<button className={clicked === 4 && "Button"} onClick={() => setClickedButton(4)}>Cancel</button>
</div>
);
};
// Render it
ReactDOM.render(
<Buttons />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<style>
.Button {
background-color: #4CAF50; /* Green */
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
</style>
<div id="react"></div>
In addition to the previous answer it's worth adding that making simple components (in our case buttons) stateful is often considered a bad practice as it gets harder to track all the state changes, and to use state from different buttons together (e.g. if you want to disable all 4 buttons in a row after any of them is pressed)
Take a look at the following implementation, where entire buttons state is contained within parent component
enum ButtonType {
ASSIGN, ABSENT, DONE, CANCEL
}
// this component is stateless and will render a button
const ActionButton = ({ label, loading, onClick }) =>
<Button type="primary" loading={loading} onClick={onClick}>
{label}
</Button>
/* inside the QueueManage component */
const [buttonsState, setButtonsState] = useState({})
const updateButton = (itemId: string, buttonType: ButtonType) => {
setButtonsState({
...buttonsState,
[itemId]: {
...(buttonsState[itemId] || {}),
[buttonType]: {
...(buttonsState[itemId]?.[buttonType] || {}),
loading: true,
}
}
})
}
const isButtonLoading = (itemId: string, buttonType: ButtonType) => {
return buttonsState[itemId]?.[buttonType]?.loading
}
return (
<ul className="listCont">
{queue.map(queueItem => (
<li className="col-12" key={queueItem.id}>
<div className="row">
<div className="listName col-3">
<p>{queueItem.user.firstName} {queueItem.user.lastName}</p>
</div>
<div className="listName col-5">
<div className="row">
<div className="col-3">
<ActionButton
label={'Assign'}
onClick={() => updateButton(queueItem.id, ButtonType.ASSIGN)}
loading={isButtonLoading(queueItem.id, ButtonType.ASSIGN)}
/>
</div>
<div className="col-3">
<ActionButton
label={'Absent'}
onClick={() => updateButton(queueItem.id, ButtonType.ABSENT)}
loading={isButtonLoading(queueItem.id, ButtonType.ABSENT)}
/>
</div>
<div className="col-3">
<ActionButton
label={'Done'}
onClick={() => updateButton(queueItem.id, ButtonType.DONE)}
loading={isButtonLoading(queueItem.id, ButtonType.DONE)}
/>
</div>
<div className="col-3">
<ActionButton
label={'Cancel'}
onClick={() => updateButton(queueItem.id, ButtonType.CANCEL)}
loading={isButtonLoading(queueItem.id, ButtonType.CANCEL)}
/>
</div>
</div>
</div>
</div>
</li>
)
)}
</ul>
)
The goal here is to keep buttons loading state in parent component and manage it from here. buttonsState is a multilevel object like
{
'23': {
[ButtonType.ASSIGN]: { loading: false },
[ButtonType.ABSENT]: { loading: false },
[ButtonType.DONE]: { loading: false },
[ButtonType.CANCEL]: { loading: false },
},
...
}
where keys are ids of queueItems and values describe the state of the 4 buttons for that item. It is usually preferred to use useReducer instead of nested spreading in updateButton but it is good to start with

Categories

Resources