Skip to content

Instantly share code, notes, and snippets.

@MrLeebo
Created May 15, 2019 15:16
Show Gist options
  • Save MrLeebo/1e5338b20b0ffad7a1d2f42e4ba1a707 to your computer and use it in GitHub Desktop.
Save MrLeebo/1e5338b20b0ffad7a1d2f42e4ba1a707 to your computer and use it in GitHub Desktop.
/// action creators ////////////////////////////////////////////////////////////
// exposing action creators as functions so the caller doesn't have to worry
// about typos when dispatching actions; e.g. dispatch(clear())
// mark in state that the refetch is being performed
export const beginFetch = payload => ({ type: 'beginFetch', payload })
// request a new refetch
export const refetch = () => ({ type: 'refetch' })
// update state with the results from a fetch
export const set = payload => ({ type: 'set', payload })
// requests a refetch and removes any requests that are not in-flight
export const clear = () => ({ type: 'clear' })
/// reducers ///////////////////////////////////////////////////////////////////
// if one big switch statement isn't your style, you can create a handler object
// which is basically just a bunch of small reducers that gets invoked by your
// main reducer.
const handlers = {
beginFetch: beginFetchReducer,
refetch: refetchReducer,
set: setReducer,
clear: clearReducer
}
function beginFetchReducer(state, {payload}) {
return { ...state, performRefetch: false, previousOptions: payload }
}
function refetchReducer(state, action) {
return { ...state, performRefetch: true }
}
// with reducers, we want the state to be immutable, which means we end up
// bending over backwards to permute the state without mutating the original
// state object. That leads to ugly code like this with lots of spread operators.
// As reducers get more complex, this strategy doesn't scale up very well.
// A library like immer can make it help when that happens
function setReducer(state, action) {
const { url, options, ...params } = action.payload
return {
...state,
requests: { ...state.requests, [requestKeyFrom(url, options)]: params }
}
}
function clearReducer(state, action) {
return {
...state,
performRefetch: true,
requests: _.filter(state, { isLoading: true })
}
}
// the primary reducer, which just invokes one of the handlers based on the type
function reducer(state, action) {
if (!handlers[action.type]) {
throw new Error(`Unrecognized action: ${action.type}`)
}
return handlers[action.type](state, action)
}
@MrLeebo
Copy link
Author

MrLeebo commented May 15, 2019

As an example of using "immer" to write a reducer, you can do something like

import produce from 'immer'

function clearReducer(state, action) {
  return produce(state, draft => {
    draft.performRefetch = true
    draft.requests = draft.requests.filter(req => req.isLoading)
  })
}

you make the changes in an imperative fashion, but immer actually proxies your changes and clones the objects you're mutating so you get behavior like the { ...state } code without having to keep track of what to clone yourself.

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