Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to include loading/progress indicators? #164

Open
morgler opened this issue Aug 4, 2017 · 4 comments
Open

How to include loading/progress indicators? #164

morgler opened this issue Aug 4, 2017 · 4 comments

Comments

@morgler
Copy link

morgler commented Aug 4, 2017

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?

@lexich
Copy link
Owner

lexich commented Aug 5, 2017

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.

@morgler
Copy link
Author

morgler commented Aug 6, 2017

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?

@morgler
Copy link
Author

morgler commented Aug 6, 2017

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 :).

@vitexikora
Copy link

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
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants