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;
Related
I have two "save" buttons which basically do the same thing.
I wanted the other one onAnotherSubmit which has a type="button" to trigger the validation.
How do i do it?
CODESANDBOX
const anotherSubmit = ({ values, errors, validateForm }) => {
validateForm().then((validation) => formik.setTouched(validation));
console.log(errors);
console.log(values);
if (!errors) {
console.log("yes submit");
}
};
<Button
variant="contained"
color="primary"
type="button"
onClick={() => anotherSubmit(formik)}
>
Save with type="button"
</Button>
ERRORCODE:
index.js:1 Warning: Instance created by `useForm` is not connected to any Form element. Forget to pass `form` prop?
I am using React with Antd and I open a modal on a button click, the button passes some data which I am capturing via "e" and render them onto the modal.
The problem is whenever the modal is closed and opened again, the contents inside do not re render, it uses the previous event data. I've figured it is because I am not using the Form properly.
I will post the code below please let me know where I have gone wrong.
import react, antd ...etc
function App () => {
const sendeventdata(e) => {
const EventContent = () => (
<div>
<Form
form={form}
labelAlign="left"
layout="vertical"
initialValues={{
datePicker: moment(event.start),
timeRangePicker: [moment(event.start), moment(event.end)],
}}
>
<Form.Item name="datePicker" label={l.availability.date}>
<DatePicker className="w-100" format={preferredDateFormat} onChange={setValueDate} />
</Form.Item>
<Form.Item name="timeRangePicker" label={l.availability.StartToEnd}>
<TimePicker.RangePicker className="w-100" format="HH:mm" />
</Form.Item>
<span className="d-flex justify-content-end">
<Button
className="ml-1 mr-1"
onClick={() => {
form
.validateFields()
.then((values) => {
onUpdate(values)
form.resetFields()
})
.catch((info) => {
console.log('Validate Failed:', info)
})
}}
>
{l.availability.updateBtn}
</Button>
<Button danger className="ml-1 mr-1" onClick={() => handleDelete()}>
Delete
</Button>
</span>
</Form>
</div>
)
}
const MyModal = () => {
const { title, visible, content } = e
return (
<Modal
onCancel={handleModalClose}
title={title}
visible={visible}
footer={null}
form={form}
destroyOnClose
>
{content}
</Modal>
)
}
return <div><MyModal /><Button onClick{sendeventdata}></Button></div>
}
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
I'm not a React expert yet thus I have a question for you - how to invoke my popup window from:
import {options, columns,convertToArray} from './consts'
const index = () => {
const {data, loading, error, performFetch} = fetchHook({path: "/xxx/yyy", fetchOnMount: true})
return (
<div className={classes.Container}>
<h1>List of products</h1>
<Divider className={classes.Divider} />
<ProductTable data={convertToArray(data)} options={options} columns={columns}/>
</div>
)
}
export default index;
consts.js
export const actions = (productPropertyId, showModal) => {
const productDetails = (productPropertyId) => {
}
const removeProduct = (productPropertyId, showModal) => {
actions(productPropertyId, showModal);
return (
<div className={classes.actionsContainer}>
<Button
onClick={() => productDetails(productPropertyId)}
> {"More"}
</Button>
<Button
const removeProduct = (productPropertyId, showModal) => {
actions(productPropertyId, showModal);
>{"Remove"}
</Button>
</div>
)
};
export const convertToArray = (productList) => {
let products = []
if (productList != null) {
productList.map(product => {
column1, column2, column3, actions(product.id)]
products.push(prod)
})
}
return products;
};
My popup is --> <FormDialog/> based on react Materials.
Is it possible to invoke popup in this place?
I have a react material table with some columns. The last column contains 2 buttons, one of them is "Remove" (row). Here I want to invoke my popup. Maybe I should rebuild my structure?
UPDATE
Below is my popup - I wonder how to run this popup from the place above:
const formDialog = (popupOpen) => {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
{/*<Button variant="outlined" color="primary" onClick={handleClickOpen}>*/}
{/* Open alert dialog*/}
{/*</Button>*/}
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Subscribe</DialogTitle>
<DialogContent>
<DialogContentText>
To subscribe to this website, please enter your email address here. We will send updates
occasionally.
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="name"
label="Email Address"
type="email"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary">
Subscribe
</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default formDialog;
UPDATE 2
I updated my code taking into cosideration the tips from your response, see above. Can I add a parameter showModal in my export const actions = (productPropertyId, showModal) and then invoke this component with different showModal value? UNfortunately my popup doesn't appear when I click on Remove button :(
You can invoke it conditionally and controle it using some state variable. Like this:
const [removeModal, removeToggle] = useState(false);
return (<>
<div className={classes.actionsContainer}>
<Button
onClick={() => productDetails(productPropertyId)}
> {"More"}
</Button>
<Button
onClick={() => removeToggle(true)}
>{"Remove"}
</Button>
</div>
{removeModal ? <YourComponent /> : ''}
</>
)
I'm using a react fragment there <></> just to position the modal div outside the main div, but you can also invoke it inside your main div as well (I usually do this and set the position: fixed so the modal/popup coud appear in top of everything).
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>