Posts CRUD like Redux Abstraction
Post
Cancel

CRUD like Redux Abstraction

I recently played the role of a front end developer. You know, I’m not one in a broad sense. I have not my head wrapped around CSS and visual stuff. But I made it reasonably well using the following toolkit:

  • ReactJS - A javascript library for building user interfaces
  • Redux - a predictable state container for JavaScript apps
  • Material-UI - A set of React Components that Implement Google’s Material Design

This combo is pretty high level and you end up with a package.json like this:

    "devDependencies": {
    "babel-jest": "^17.0.2",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-react": "^6.16.0",
    "jest": "^17.0.3",
    "react-scripts": "0.7.0",
    "react-test-renderer": "^15.4.0",
    "redux-mock-store": "^1.2.1"
  },
  "dependencies": {
    "es6-promise": "^4.0.5",
    "firebase": "^3.5.3",
    "flexboxgrid": "^6.3.1",
    "formsy-material-ui": "^0.5.3",
    "formsy-react": "^0.18.1",
    "isomorphic-fetch": "^2.2.1",
    "jquery": "^3.1.1",
    "lodash": "^4.17.2",
    "material-ui": "^0.16.1",
    "query-string": "^4.2.3",
    "react": "^15.3.2",
    "react-addons-css-transition-group": "^15.3.2",
    "react-dom": "^15.3.2",
    "react-flip-move": "^2.6.4",
    "react-materialize": "^0.17.0",
    "react-redux": "^4.4.5",
    "react-router": "^3.0.0",
    "react-tap-event-plugin": "^1.0.0",
    "redux": "^3.6.0",
    "redux-thunk": "^2.1.0"
  },

Most of it was generated by Create React App because to me it is so much to setup, that I picked up the easy way. You can use Webpack - I once used to setup/build a Angular 2 app - but it’s totally non standardized and every example on the internet just look totally different from each other.

From this package.json above, I would like to quickly highlight redux-thunk, Lodash and isomorphic-fetch. They are must have for every Redux/React apps!

Redux

The main point of this post is Redux (Hold on, it’s not another Redux introduction, if you want to understand it see this blog post by Tal Kol). I like it and should use it every app with ReactJS from now on, there are couple alternatives that I haven’t tried, like Fluxible, Alt and Relay. But Redux is good to go as long you are ready to write tons of code.

The repetition from Redux came into play when I had to fetch different entities from the server and for all of them, keep track of at least 3 states: LOADING, RECEIVE and FAILED. You can call it with different names, but they express something like this.

If you are familiar with Redux you know it will requrie a reducer case, an action creator function, and a action type constant. And of course the same series of dispatch for every entity your are trying to load form the server. Something like:

 // Suppose this is a thunk for fetching things from the server
return async function(dispatch) {
    try {
        dispatch({type: 'ENTITY_LOADING' })
        let response = await this.fetchService(localStorage.token, pk)
        dispatch({type: 'ENTITY_RECEIVE', data: 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 {
            dispatch({type: 'ENTITY_FAILED', pk: pk})
        }
    }
}

This pattern repeats all over the app. The abstraction code is pretty big, please refer to the full source at THIS GIST. It’s called ReduxFetcher.

To summarise it, the code creates action type names out of entity argument. I’ve also used specific action type names for every operation.

    // ...

        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`

    // ...

And action creators that refer to these actions when needed:

    // ...

    listFailure(dispatch) {
        dispatch({
            type: this.listingFailedActionType
        })
    }

    listReceive(dispatch, data) {
        dispatch({
            type: this.listingReceiveActionType,
            data: data
        })
    }

    // ...

Finally, the abstraction provides fetch, filter, list and update dynamic actions. See the list implementaion:

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

The result was a high level of code being reused for 8 entities. The abstrated actions can also be wired to componetes using mapdispatchToProps and used as any function inside components.

Conclusion

Redux is awesome, I feel I do better Flux than used to do MVC in the old times of JSP, Spring, Struts, JSF, etc. You feel not only that you understood whole Flux, but also you are able to fully implemented it.

Of course my abstraction got a problem, it is mine in the sense that it was written/designed to work with my application. After finishing the project I realized there are couple options out there to do what I did:

It was ok for me to find those libs only at the end of the project (went live already), it gave me the sensation that I wasn’t so wrong trying to aggregate and centralize these Redux pieces in the same place.

Hope you liked! Cheers!

This post is licensed under CC BY 4.0 by the author.