Last active
January 6, 2021 05:52
-
-
Save paulosuzart/e6d9e24c58e099e2f5c6d9b59591a253 to your computer and use it in GitHub Desktop.
a simple abstraction for fetching, updating and filtering objects with correspondending state.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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