redux-api
redux-api copied to clipboard
How to include loading/progress indicators?
Sorry for the stupid question, but it seems I have to write a lot of code to simply show a loading indicator with redux-api. Is there a recommended approach?
What I tried:
Approach 1:
Show and hide a loading indicator depending on the state of the metadata loading
attribute. This approach takes a lot of code, because each resource in redux-api
has its own loading
attribute.
Approach 2:
Dispatch SHOW/HIDE actions to toggle the loading indicator. I implemented this using the prefetch
and postfetch
hooks.
prefetch: [
function({actions, dispatch, getState}, cb) {
dispatch(showLoading())
cb()
}
],
postfetch: [
function({data, actions, dispatch, getState, request}) {
dispatch(hideLoading())
}
]
But again I have to do this for each resource separately, which adds a lot of duplicated code to my reduxApi
definition. I also have to deal with the callback chain, which seems odd, if I want to handle a loading indicator.
How do you guys do it? There must be a simple way to show a loading indicator with redux-api
, right?
Hi @morgler For show/hide loading progress there is project redux-api-react-switch
If you want to show global loading progress, maybe the best to catch all redux-api actions in reducer
function(action, state) {
if (/^@@redux-api*_(success|fail)/.test(action.type) {
state = hideLoading(state)
}
....
}
This solution isn't very beautiful, but efficient.
Thanks for the tips. Something like redux-api-react-switch is NOT what I want. I don't want to scatter loading-bar code among all my components. I prefer to have this loading-bar behavior capsuled into one component and never have to think about it again.
Your second suggestion sounds nice, though not very beautiful as you noted ;). The code that actually catches the event with refined regex (you forgot a ".") is:
const loadingBar = (state = {}, action) => {
if (/^@@redux-api.*_(success|fail)$/.test(action.type)) {
hideLoading()
} else if (/^@@redux-api.*/.test(action.type)) {
showLoading()
}
return state
}
However, this gives me the problem of having to dispatch actions from within a reducer – which is usually not a good practice. Sure, this thing is not a "real" reducer, but still. Anyway, this seems to be the solution I will go with.
Overall I find it rather amazing, that including a loading indicator has never been an issue for anyone else when using redux-api. Is there really no better solution?
What I ended up doing now as a workaround is this:
const defaultOptions = {
transformer: function(data, prevData, action) {
data || (data = {})
return data.data
},
prefetch: [
function({actions, dispatch, getState}, cb) {
dispatch(showLoading())
cb()
}
],
postfetch: [
function({data, actions, dispatch, getState, request}) {
dispatch(hideLoading())
}
]
}
export default reduxApi({
bands: Object.assign({}, defaultOptions, {
url: `/api/bands.json`
}),
…
})
So I factored all the default options into a hash that I simply merge into every endpoint definition in reduxApi
. To me this looks like the most elegant solution for now. My API definition stays clean and readable and I don't have to mess with regex or stuff polluting my store outside of my API definition :).
I am using this reducer:
import assign from 'lodash/assign'
import rest from 'api/rest'
/**
* This reducer watches all rest actions and produces an object like
* {action1: false, action2: false, activeAction3: true, ...}
* so you can read current syncing state anywhere like state.dataSyncing.YOUR_ACTION
*/
const allHandles = Object.keys(rest.actions)
const initialState = allHandles.reduce(
(res, val) => {
res[val] = false
return res
}, {})
export default function dataSyncing(state = initialState, action) {
if (typeof action.type !== 'string' || action.type.indexOf('@@redux-api@') !== 0) return state
const match = action.type.match(/^@@redux-api@(.*?)(_(?:success|fail))?$/)
const handle = match[1]
if (allHandles.indexOf(handle) >= 0) {
const syncing = typeof match[2] === 'undefined'
return assign({}, state, {[handle]: syncing})
} else {
return state
}
}