Do events (DOM events or system events) have a 1:1 relationship with actions? i.e. should a single click event trigger only one action?
For example, let's say we have a page which displays a table of 10 rows and 2 columns. Each row has a Product field and an Amount field. The Amount field has a range input with a range of [0, 10]. The user can set the Amount of each Product individually.
The user is also given 2 options, through the use of 2 buttons.
- Pressing the second button will disable all but the first product in the table (effectively setting their Amount to 0 and the user can no longer interact with them to set their Amount). Let's call this
Option B
- Pressing the first button enables all Products after the first (by default setting their Amount to 1 for each of them) and the user can once again interact with them, to set their amounts individually. Let's call this
Option A
.
Option A selected: | PRODUCT | AMOUNT | |------------------|-----------| | Product A | - 4 + | | Product B | - 0 + | | Product C | - 4 + | ```````````````````````````````` _________ | Option A| OPTION B ````````` Option B selected: | PRODUCT | AMOUNT | |------------------|-----------| | Product A | - 4 + | | Product B | Disabled | (Amount == 0) | Product C | Disabled | (Amount == 0) ```````````````````````````````` _________ OPTION A | OPTION B| ````````` Option A selected again: | PRODUCT | AMOUNT | |------------------|-----------| | Product A | - 4 + | | Product B | - 1 + | | Product C | - 1 + | ```````````````````````````````` _________ | Option A| OPTION B `````````
The state of this 'app' is described by this simple object
state = {
option : <String>,
products : [
{
name : <String>,
amount : <Integer>
}, ...
]
}
We also have these 4 simple action creators:
function setOption(option) {
return { type : 'SET_OPTION', option : option};
}
function incAmount(productName) {
return {
type : 'INCREMENT_AMOUNT',
product : productName
}
}
function decAmount(productName) {
return {
type : 'DECREMENT_AMOUNT',
product : productName
}
}
function setAmount(productName, amount) {
return {
type : 'SET_AMOUNT',
payload : { product : productName, amount : amount }
}
}
For the sake of simplicity, we have only one reducer.
In this example, selecting Option B
should have the following effects on the state :
- Change
option
toB
- Set the amount of every
product
after the first to0
Selecting Option A
should have the following effects on the state, respectively :
- Change
option
toA
- Set the amount of every
product
after the first to1
Incrementing the amount of Product A should have the following effects on the state :
- Increment the amount of Product A by 1
What would be the proper way to implement these changes?
a) Have the onClick
handler of the option
buttons do the following:
- Fire a
store.dispatch(setOption(option))
- For each product after the first one fire a
store.dispatch(setAmount(productName, amount))
(amount
= 1 for option A, 0 for option B)
b) Have the onClick
handler of the option
buttons do the following:
Fire a
store.dispatch(setOption(option))
And have the reducer change the
option
as well as theamount
of every product after the first one to the specified amount (amount
= 1 for option A, 0 for option B)
If we go with a) each case in the switch (action) {}
statement of the reducer deals with just one aspect of the state, but we have to fire more than one action from one click
event
If we go with b) we fire only one action from the click
event but the case for SET_OPTION
in the reducer not only changes the option
but also the amount
of products.