Skip to content

Instantly share code, notes, and snippets.

@paulosuzart
Last active January 6, 2021 05:52
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paulosuzart/e6d9e24c58e099e2f5c6d9b59591a253 to your computer and use it in GitHub Desktop.
Save paulosuzart/e6d9e24c58e099e2f5c6d9b59591a253 to your computer and use it in GitHub Desktop.
a simple abstraction for fetching, updating and filtering objects with correspondending state.
import {
XPTOService
} from '../services'
import { loginFailed } from './index.js'
const initial = {
filtering: {
data: []
},
listing: {}
}
/**
* Abstraction on top of redux-thunk using some conventions for fetching, filtering and updating a entity.
*/
class FetchReduxer {
constructor(options = { persistLocalStore: false }) {
let {entity, fetchService, updateSerivce, listService, persistLocalStore, filterService} = options
this.actionPrefix = this.constructor.name;
this.persistLocalStore = persistLocalStore
this.entity = entity
this.listService = listService
this.filterService = filterService
this.fetchService = fetchService
this.updateSerivce = updateSerivce
this.listingActionType = `${this.actionPrefix}_${entity}_LIST_LOAD`
this.listingFailedActionType = `${this.actionPrefix}_${entity}_LIST_FAILED`
this.listingReceiveActionType = `${this.actionPrefix}_${entity}_LIST_RECEIVE`
this.loadingActionType = `${this.actionPrefix}_${entity}_LOAD`
this.failedActionType = `${this.actionPrefix}_${entity}_FAILED`
this.receiveActionType = `${this.actionPrefix}_${entity}_RECEIVE`
this.updatingActiontype = `${this.actionPrefix}_${entity}_UPDATING`
this.updatingSuccessActiontype = `${this.actionPrefix}_${entity}_UPDATING_SUCCESS`
this.updatingFailedActiontype = `${this.actionPrefix}_${entity}_UPDATING_FAILED`
this.filteringActionType = `${this.actionPrefix}_${entity}_filtering`
this.filteringSuccessActionType = `${this.actionPrefix}_${entity}_filtering_SUCCESS`
this.filteringFailedActionType = `${this.actionPrefix}_${entity}_filtering_FAILED`
this.finishActionType = `${this.actionPrefix}_${entity}_FINISH`
this.entityStatusKey = `${entity}Status`
this.fetch = this.fetch.bind(this)
this.reducer = this.reducer.bind(this)
this.update = this.update.bind(this)
this.list = this.list.bind(this)
}
listing(dispatch) {
dispatch({
type: this.listingActionType
})
}
listFailure(dispatch) {
dispatch({
type: this.listingFailedActionType
})
}
listReceive(dispatch, data) {
dispatch({
type: this.listingReceiveActionType,
data: data
})
}
/**
* Action creator that can be used to clean up state for screens that have componentWillUnmount callback implemented
*/
finish(dispatch) {
dispatch({
type: this.finishActionType
})
}
/**
* Marks the entity as being loaded
*/
loadingAction(dispatch) {
dispatch({
type: this.loadingActionType
})
}
/**
* Mark the entity as received. The payload is available at `data` key of the action
*/
receiveAction(dispatch, data) {
dispatch({
type: this.receiveActionType,
data: data
})
}
/**
* Mark the entity as failed while trying to loead.
*/
failAction(dispatch, pk) {
dispatch({
type: this.failedActionType,
pk: pk
})
}
/**
* The entity is starting to get updated
*/
updatingAction(dispatch, pk) {
dispatch({
type: this.updatingActiontype,
pk: pk
})
}
/**
* Update succeeded
*/
updatingSuccess(dispatch, pk, data) {
dispatch({
type: this.updatingSuccessActiontype,
pk: pk,
data: data
})
}
/**
* Failure detected while updating the entity
*/
updatingFailed(dispatch, pk) {
dispatch({
type: this.updatingFailedActiontype,
pk: pk
})
}
/**
* The reducer itself. Must be called with `combineReducers`.
*/
reducer(state = initial, action) {
let tmp = {}
switch (action.type) {
case this.listingActionType: {
return Object.assign({}, state, {
listing: {
status: 'LOADING'
}
})
}
case this.listingFailedActionType: {
return Object.assign({}, state, {
listing: {
status: 'FAILED'
}
})
}
case this.listingReceiveActionType: {
return Object.assign({}, state, {
listing: {
status: 'SUCCESS',
data: action.data
}
})
}
case this.finishActionType: {
return initial
}
case this.filteringActionType:
return Object.assign({}, state, {
filtering: {
data: state.filtering.data,
status: 'LOADING'
}
})
case this.filteringSuccessActionType:
return Object.assign({}, state, {
filtering: {
data: action.data,
status: 'LOADED'
}
})
case this.filteringFailedActionType:
return Object.assign({}, state, {
data: [],
status: 'FAILED'
})
case this.loadingActionType:
return Object.assign({}, state, {
data: null,
status: 'LOADING'
})
case this.failedActionType:
return Object.assign({}, state, {
data: null,
status: 'FAILED'
})
case this.receiveActionType:
return Object.assign({}, state, {
data: action.data,
status: 'LOADED'
})
case this.updatingActionType:
return Object.assign({}, state, {
updating: {
pk: action.pk,
status: 'UPDATING'
}
})
case this.updatingFailedActiontype:
return Object.assign({}, state, {
updating: Object.assign({}, state.updating, {
status: 'FAILED'
})
})
case this.updatingSuccessActiontype:
return Object.assign({}, state, {
updating: Object.assign({}, status.updating, {
status: 'UPDATED'
})
})
default:
return state
}
}
/**
* The entity is being filtered
*/
filtering(dispatch) {
dispatch({
type: this.filteringActionType
})
}
/**
* Filter succeeded. Payload available in the `data` key
*/
filteringSuccess(dispatch, data) {
dispatch({
type: this.filteringSuccessActionType,
data: data
})
}
/**
* Failure detecter while filtering object
*/
filteringFailed(dispatch) {
dispatch({
type: this.filteringFailedActionType
})
}
/**
* redux thunk for actually ivoking the filter service and appropriate actions
*/
filter(query) {
return async function(dispatch) {
try {
this.filtering(dispatch)
let response = await this.filterService(localStorage.token, query)
this.filteringSuccess(dispatch, response)
} catch (ex) {
console.log(ex)
if (ex instanceof LoginRequired) {
dispatch(loginFailed('Falha na Autenticação'))
} else {
this.filteringFailed(dispatch)
}
}
}.bind(this)
}
/**
* redux thunk for actually ivoking the update service and appropriate actions
*/
update(pk, data) {
return async function(dispatch) {
try {
this.updatingAction(dispatch)
let response = await this.updateSerivce(localStorage.token, pk, data)
this.updatingSuccess(dispatch)
} catch (ex) {
console.log(ex)
if (ex instanceof LoginRequired) {
dispatch(loginFailed('Falha na Autenticação'))
} else {
this.updatingFailed(dispatch)
}
}
}.bind(this)
}
list() {
return async function(dispatch) {
try {
this.listing(dispatch)
let response = await this.listService(localStorage.token)
this.listReceive(dispatch, response)
} catch (ex) {
console.log("Exception in Fetcher Redux " + this.entity)
console.log(ex)
if (ex instanceof LoginRequired) {
dispatch(loginFailed('Falha na Autenticação'))
} else {
this.listFailure(dispatch)
}
}
}.bind(this)
}
/**
* redux thunk for actually ivoking the fetch service and appropriate actions
*/
fetch(pk) {
return async function(dispatch) {
try {
this.loadingAction(dispatch)
let response = await this.fetchService(localStorage.token, pk)
this.receiveAction(dispatch, response)
if (this.persistLocalStore) {
localStorage.setItem(this.entity, JSON.stringify(response))
}
} catch (ex) {
console.log("Exception in Fetcher Redux " + this.entity)
console.log(ex)
if (ex instanceof LoginRequired) {
dispatch(loginFailed('Falha na Autenticação'))
} else {
this.failAction(dispatch, pk)
}
}
}.bind(this)
}
}
// Sampe usage
export const xptoReduxer = new FetchReduxer({
entity: 'prescription',
fetchService: XPTOService.getData,
updateSerivce: XPTOService.updateData,
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment