React toggle className on button click - reactjs

I wanna change my layout between grid and list with 2 buttons. I am very new to react, and I thought to use State, my button logic seems to work(when i console.log) but I don't know how to change div classNames based on State. I tried something but it doesnt work(see below). Whatever button I click grid or list my style changes but only for the first click, then nothing happens.
const Restaurants = () => {
const [isGrid, layoutToggle] = useState({
value: true
});
console.log(isGrid);
return (
<div className="restaurants">
<button onClick={() => layoutToggle({ isGrid: true })}>
Grid
</button>
<button onClick={() => layoutToggle({ isGrid: false })}>
List
</button>
<div className={isGrid["value"] ? "layout-grid" : "layout-list"}>
{restaurants.map(restaurant => (
<Restaurant/>
))}
</div>
</div>
);
};

The problem is in the button elements, You should the change the state with the value property not the isGrid property.
<button onClick={() => layoutToggle({ value: true })}>
Grid
</button>
<button onClick={() => layoutToggle({ value: false })}>
List
</button>

Related

I make a button base on a state, but how to make them listen to different event in React?

I'm currently working on a small project
I rendered a button base on a state like these two images,
edit button
save change button
here is my code: but everytime when I click on the edit button it will automatically run the submit button too and the isEdit state can never change to true, like this, problem, please let me know how to solve it. thanks!
const [isEdit, setIsEdit] = useState(true)
return(
...
{isEdit ? (
<button
className="btn btn--fill-primary"
type="submit"
disabled={isLoading}
onSubmit={() => {
console.log("submit")
setIsEdit(false)
}}
>
save change
</button>
) : (
<button
className="btn btn--fill-black"
type="button"
disabled={isLoading}
onClick={() => {
console.log("click")
setIsEdit(true)
}}
>
edit
</button>
)}
)
at first I render the button like this:
<button
className={`btn btn--fill-${isEdit ? "primary" : "black"}`}
type={isEdit ? "submit" : "button"}
disabled={isLoading}
onClick={(e) => {
if (isEdit) {
console.log('submit')
setIsEdit(false)
}
if (!isEdit) {
console.log('click')
setIsEdit(true)
}
>
{isEdit ? "save change" : "edit"}
</button>

Looking for suggestions for a better solution

So I have a site which has 12 buttons. 6 of these buttons will need to launch a popup with a different video and 6 buttons will need to launch a popup with 6 images.
Now, currently, I am experimenting with a single popup displaying an image. And this is fairly easy, I set a state
const [showModal, setShowModal] = useState(false);
And then I have a button which will set the show onClick and then a model is shown - eg
<button className="buttonGeneral" onClick={()=>setShowModal(true)}>SUMMARY11</button> <ModalReact showModal={showModal} onClose={() => setShowModal(false)} image={props.image_1} size='med'/>
Thinking about this, I am thinking I will need 2 different types of modals, one for video and one for images. But with the approach above, I am going to need to useState with 12 different states. This seems a bit wrong, but I cannot think of any other way.
For completeness, here is the ModalReact component
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
import '../styles/react-bs.css';
function ModalReact({showModal = false, onClose = () =>{}, image, size}) {
console.log("im ", image)
return (
<Modal
size={size}
show={showModal}
onHide={onClose}
backdrop="static"
keyboard={false}
// dialogClassName="videoPopup"
>
<Modal.Body><img src={image} alt="lala"></img></Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={onClose}>
Close
</Button>
</Modal.Footer>
</Modal>
)
}
export default ModalReact
You need to maintain different states for all the buttons. I would suggest writing a custom component with its state maintained in the same component so that it's available for only that component. Based on that, we can open a modal from its state variable.
export default function ModalReact({ itemNum }) {
const [showModal, setShowModal] = useState(false);
const handleClose = () => setShowModal(false);
const handleShow = () => setShowModal(true);
return (
<>
<Button variant="primary" onClick={handleShow}>
Launch demo modal {itemNum}
</Button>
<div style={{ display: "block", width: 700, padding: 30 }}>
<Modal show={showModal} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Modal heading {itemNum}</Modal.Title>
</Modal.Header>
<Modal.Body>
Woohoo, you're reading this text in a modal! {itemNum}
{itemNum > 6 ? "image" : "video"}
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</div>
</>
);
}
We can use that from any component. You said you have 12 buttons so iterate and display all those buttons. I have used a simple array and iterating to display all the buttons.
return (
<div className="container">
{Array.from({ length: 12 }, (_, i) => i + 1).map((item) => (
<CustomModal itemNum={item} />
))}
</div>
);
Attached is a sandbox for reference.

Disable button after click in React

I have this piece of code but can't work out how I can disable the button after Pay is clicked:
} else if (paymentMethod.name === "Pay by Cash") {
return (
<Tab.Pane eventKey={paymentMethod.id} key={key}>
<h6 className="mb-3 mt-0 mb-3">Cash</h6>
{this.state.paymentMethodSelectionError && this.state.paymentMethodSelectionError !== "" && <Alert key="error_div" variant="danger">{this.state.paymentMethodSelectionError}</Alert>}
<p>You are paying by cash</p>
<Form.Group className="mb-0">
<Button className="btn btn-success btn-block btn-lg"
onClick={() => {
this.setState({
selectedPaymentMethod: "not-charged",
paymentGateway: paymentMethod.id
}, () => {
this.setPaymentMethod()
})
}}>
PAY £{this.state.totalPay}<Icofont icon="long-arrow-right" /></Button>
</Form.Group>
</Tab.Pane>)
Any help is appreciated
You can put a state for disabling your button:
this.state = {
disabled: false
}
In click function, change it to true:
const clickButton = () => {
this.setState({ disabled: true });
}
Then change your HTML to something like this:
<button type="button" disabled={ this.state.disabled }>Click Me!</button>
Preview:
Here
Declare a new state variable to take care of the disabled button:
const [buttonState,setButtonState]=useState(false)
Attach a onClick handler to disable the button and also manage the disabled property of the button.
<button onClick={()=>{ setButtonState(true)}} disabled={buttonState}> PAY </button>
You can use state to control the button state
Preview for both Functional and Class components
Codesandbox Preview Link

Update useState onclick not updating

I am trying to update a useState object, my goal is to toggle the buttons and keep which button is on and which is off in the useState.
const SalesChartCard = (data) => {
const [toggleButton, toggleButtons] = useState({
'impressions': true,
'purchasestart': true,
'purchasecancel': true,
'purchasecomplete': true
});
const buttonClick = (dimension) => {
toggleButtons({
dimension: !toggleButton.dimension
});
};
return (
<>
<Button
outline={toggleButton.impressions}
onClick={e => buttonClick('impressions')}
color="primary" className="mb-2">
Impressions
</Button>
<Button
outline={toggleButton.purchasestart}
onClick={e => buttonClick('purchasestart')}
color="secondary" className="mb-2">
Purchase Start
</Button>
<Button
outline={toggleButton.purchasecancel}
onClick={e => buttonClick('purchasecancel')}
color="danger" className="mb-2">
Purchase Cancel
</Button>
<Button
outline={toggleButton.purchasecomplete}
onClick={e => buttonClick('purchasecomplete')}
color="success" className="mb-2">
Purchase Complete
</Button>
</>
);
};
export default SalesChartCard;
The useState react hook doesn't shallow merge state updates, you need to manage this yourself.
For the given click handler: onClick={e => buttonClick('impressions')}
The buttonClick callback handler should merge existing state and use dynamic (computed property name) keys for updating the correct property. Use a functional state update to correctly compute the next state from the previous state. This ensures all previous state updates are correctly merged when updated.
const buttonClick = (dimension) => {
toggleButtons(prevState => ({
...prevState,
[dimension]: !prevState[dimension],
}));
};
Your state is an object. When you update an object you need to keep the previous value as well. So instead of creating a new object with your dimension key you have to use object spread to keep the previous state.
Besides that as your key is in a variable you need to use key inside [].
You can do something like this,
const SalesChartCard = (data) => {
const [toggleButton, toggleButtons] = useState({
'impressions': true,
'purchasestart': true,
'purchasecancel': true,
'purchasecomplete': true
});
const buttonClick = (dimension) => {
toggleButtons({
...toggleButton,
[dimension]: !toggleButton[dimension]
});
};
return (
<>
<Button
outline={toggleButton.impressions}
onClick={e => buttonClick('impressions')}
color="primary" className="mb-2">
Impressions
</Button>
<Button
outline={toggleButton.purchasestart}
onClick={e => buttonClick('purchasestart')}
color="secondary" className="mb-2">
Purchase Start
</Button>
<Button
outline={toggleButton.purchasecancel}
onClick={e => buttonClick('purchasecancel')}
color="danger" className="mb-2">
Purchase Cancel
</Button>
<Button
outline={toggleButton.purchasecomplete}
onClick={e => buttonClick('purchasecomplete')}
color="success" className="mb-2">
Purchase Complete
</Button>
</>
);
};
export default SalesChartCard;

Stop button nested inside a form from submitting and have it fire a different function

I have a close button nested inside a form that is supposed to reset the form and close the popup modal the form is sitting on.
I've changed the button type from the default submit to type='reset' and have an onClick that is supposed to fire my resetting/closing function, but the function is never being called and instead the submit function is called.
is that not possible with jsx or what am I missing?
(Note that Button is just a component wrapping a <button> html tag)
import Button from '../../../components/Button'
export const SomeForm = (props) => (
<form className='quote-form__wrapper' onSubmit={props.onSubmit}>
<Button
className='close-btn'
type='reset'
onClick={props.resetForm}
value='CLOSE' text='x' />
some form fields...
<Button
className='btn ripple-btn'
type='submit'
text={props.btnText} />
</form>
)
EDIT:
I should add that when I don't use a custom Button component to render the button element it does work even though I don't change the button type the right function (resetForm) is being called:
const someForm = (props) => (
<form onSubmit={props.onSubmit}>
<div>
<button className='close-btn' onClick={props.resetForm} value='CLOSE'>
x
</button>
some form fields...
<button
className='ripple-btn btn'
type='submit'>
{props.btnText}
</button>
</div>
</form>
)
For what it's worth the Button component looks like this:
const Button = (props) => (
<button className={props.className} {...props.attributes}>
{props.text}
</button>
)
export default Button
You aren't passing down the other props properly in your Button component. You should use {...props} and not {...props.attributes}.
const Button = (props) => (
<button className={props.className} {...props}>
{props.text}
</button>
);
export default Button;
Any props that you don't want to pass directly to the button could be obtained from the props using object destructuring:
const Button = ({text, className, ...props}) => (
<button className={className} {...props}>
{text}
</button>
);

Resources