Putting all together
So far so good. You've read about a lot of different concepts. State-Events, sagas, concurrencies, cancelation, rule-nesting... It's time to put all together. The following is a really complex example, so please read the docs again if anything is not clear enough.
The Stage
We have a list of products. Each product has a size and we are able to filter our products by size. To do so the user can open up a dropdown to select one or multiple sizes:
We have products reducer that is responsible for holding the product list and managing the filters. This module exports an action-creator that can update the filters:
const SET_FILTER = 'products/SET_FILTER'
const setFilter = (filterKey, filterValue) => ({
type: SET_FILTER,
meta: { filterKey },
payload: filterValue
})
And we have a rule that fetches the new products whenever we set a filter:
addRule({
id: 'products/FETCH',
target: FETCH_PRODUCTS_REQUEST,
concurrency: 'SWITCH',
consequence: ({getState}) => {
const state = getState()
const filters = getFilters(state.products)
return api.fetchProducts(filters).then(
result => actions.fetchProductsSuccess(result),
error => actions.fetchProductsFailure(error)
)
}
})
addRule({
id: 'products/TRIGGER_FETCH',
target: [SET_FILTER, SET_PAGE, SET_CATEGORY],
consequence: () => actions.fetchProductsRequest()
})
Our FilterDropdown:
import {dispatchEvent} from 'redux-ruleset'
const FilterDropdown = ({ filterKey, filterValue, options }) => {
return (
<Dropdown
onOpen={() => dispatchEvent({ type: 'FilterDropdown/OPEN', meta: {filterKey} })}
onClose={() => dispatchEvent({ type: 'FilterDropdown/CLOSE', meta: {filterKey} })}
activeValue={filterValue}
options={options}
/>
)
}
Requirements
It is unneccessary to trigger a request while the user is still refining in the dropdown. So we only fetch when the the dropdown closes
- We do not want to trigger a request while the Dropdown is open
- When the user closes the dropdown without setting a filter we do not want to trigger a request
- As soon as the user sets one or multiple filters we trigger a request after the dropdown closes
The rule
import {addRule, skipRule} from 'redux-ruleset'
import * as productActions from 'modules/products/actions'
/*
When the user opens a filter-dropdown
Then we want to disable all product requests
and trigger one, as soon as the user refines and closes the dropdown
*/
addRule({
id: 'feature/FETCH_ON_DROPDOWN_CLOSE',
target: 'FilterDropdown/OPEN',
addUntil: function* (next) {
const action = yield next(['FilterDropdown/CLOSE', 'FilterDropdown/OPEN'])
if(action.type === 'FilterDropdown/OPEN'){
return 'RECREATE_RULE_BEFORE'
}
return 'RECREATE_RULE'
},
consequence: (action, {addRule}) => {
addRule('preventSearch')
addRule('triggerSearch')
},
subRules: {
preventSearch: {
target: 'products/FETCH_REQUEST',
position: 'INSTEAD',
consequence: () => null
},
triggerSearch: {
target: 'FilterDropdown/CLOSE',
addWhen: function* (next) {
const action = yield next(['products/SET_FILTER'])
return 'ADD_RULE'
},
consequence: () => {
const action = productActions.fetchProductsRequest()
return skipRule('feature/FETCH_ON_DROPDOWN_CLOSE', action)
}
}
}
})
Quite a few things happen here, so let's analyze this rule step by step. The target
key tells us that this rule is invoked whenever a FilterDropdown/OPEN state action is dispatched. Then we want two things to happen:
- all
products/FETCH_REQUEST
actions should be discarded - when the user refines, we want to fetch after the dropdown closes
To prevent all requests we add the subRule preventSearch. This rule listens to the target
products/FETCH_REQUEST and totally throw it away (position
INSTEAD and consequence
returns null). That means that no products/FETCH_REQUEST will get dispatched as long as this rule is active.
The next rule is resposible for fetching after the dropdown closes. The addWhen
saga tells us, that we wait for the next products/SET_FILTER. If the user sets a filter we want to add the rule.
When the second rule is added, it listens for the next FilterDropdown/CLOSE state event. As a consequence we will dispatch a product-request.