Reputation: 995
I am working on transactions table. I just want to add, remove and update the transactions in the table.
I have a 'Transactions.js' functional component which has the state of transactions (using hooks) and I also have a 'TransactionsForm.js' component which receives 'setTransactions' function as a prop from 'Transactions.js' to add or update the transactions state
Transactions.js
const [transactions, setTransactions] = useReducer(
transactionsReducer,
fetchTransactions()
)
TransactionsForm.js
const [values, setValues] = useState({
description: '',
transactionType: '',
date: '',
category: '',
amount: ''
})
const handleChange = event => {
const { name, value } = event.target
setValues({ ...values, [name]: value })
}
const handleAdd = () => {
setTransactions({
type: 'add',
transactionType: 'income',
description: values.description,
date: values.date,
category: values.category,
amount: values.amount
})
}
const handleUpdate = () => {
setTransactions({
type: 'update',
id: 1,
transactionType: 'income',
description: values.description,
date: values.date,
category: values.category,
amount: values.amount
})
}
const handleSubmit = event => {
event.preventDefault()
// 1. handleUpdate() if the user is updating an existing transaction
// 2. handleAdd() if the user is adding new transaction
}
As you can see I have both functions, handleAdd and handleUpdate. but I need to call one of them according to the user's action (update or add) and also if it is update, I need to get transaction id.
What is the best way to accomplish this? I know that I shouldn't create two separate forms for add and update
Upvotes: 1
Views: 2331
Reputation: 1164
Usually I create one form to handle both add and update. You have to create your form that way to accept default dataset and if that is provided just load them into the proper control field.
My controls
props for this form looks like this:
const controls = [
{
key: 'supa-dupa-key',
type: 'text',
label: 'whatever',
required: true,
alertText: 'Not today...',
disabled: false,
defaultValue: ''
}
];
I use this to fill up my common form local state, if you want to use the form to add something just pass the controls without defaultValue
and if you want to use the form to update just fill up the controls defaultValue
property:
fillUp = controls => ({
...controls.reduce((o, { key, defaultValue, type }) => ({
...o,
[key]: {
value: defaultValue || '',
isValid: false,
isInvalid: false
}
}), {})
});
Each control field will get the value
and the onChange
based on the control key:
value={this.state[key]}
onChange={e => this.handleChange(e.target.value, key)}
Also, I use dynamic control rendering to render each control from the controls prop. Something like this (create as many type as you want):
renderTextControl = control => (
<Form.Group key={control.key} controlId={control.key}>
<Form.Label className="mb-1 text-muted">
{control.label}{control.required && ' *'}
</Form.Label>
<Form.Control
disabled={this.props.inProgress || control.disabled || false}
required={control.required || false}
type={type}
size="sm"
isValid={this.state[control.key].isValid}
isInvalid={this.state[control.key].isInvalid}
value={this.state[control.key].value}
onChange={this.handleChange(control.key, control.type)}
/>
{control.alertText &&
<Form.Control.Feedback type="invalid">
{control.alertText}
</Form.Control.Feedback>
}
</Form.Group>
);
I hope this can help you and you can adapt this to your needs.
Upvotes: 3