/ Miscellaneous

A helper to handle REST Apis with Redux in react-native

A helper to handle REST Apis with Redux in react-native

react-native-redux-rest-resource

A helper to handle REST Apis with Redux in react-native.

How to set it up

// resources.js
import Resource from 'react-native-redux-rest-resource';

export const PostsResource = Resource(
    'post',
    {
        API_URL: 'https://mygreat.api.com/v1/', // Define the URL of your API
        HTTP_HEADERS: { X-MOBILE-TOKEN: 'j3rkl2uh44u4uys87w3874y3rt3487wrtyw87' }, // Set extra headers
    }
);
// store.js
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import { Platform } from 'react-native';
import devTools from 'remote-redux-devtools';
import Thunk from 'redux-thunk';
import { PostsResource } from './resources';
import PostsReducer from './postsReducer';

const enhancer = compose(
    applyMiddleware(Thunk, PostsResource.middleware),
    devTools({
        hostname: 'localhost',
        port: 28000,
        name: `MyAPP_${Platform.OS}`,
    })
);

export default createStore(
    combineReducers({
        Posts: PostsReducer,
    }),
    enhancer
);

How to trigger RESTful Requests

// TestClass.js
import React, { Component } from 'react';
import {
    ActivityIndicator,
    StyleSheet,
    Text,
    View,
} from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { PostsResource } from './resources';

const styles = StyleSheet.create({
    container: {}
});

class TestClass extends Component {
    componentDidMount() {
        this.props.index(); // Get all the posts
        this.props.create({ content: 'Hello' }); // Create a post
        setTimeout(() => this.props.read(this.props.posts[0].id), 5000); // Get a post
        setTimeout(() => this.props.update({ content: 'Hello There!' }, 1), 10000); // Create a post
        setTimeout(() => this.props.delete(1), 20000); // Delete the post of ID 1
    }

    render() {
        return (
            <View style={styles.container}>
                {!this.props.loading && !this.props.error
                    ? this.props.posts.map((post) => (
                        <View key={post.id}>
                            <Text>{post.content}</Text>
                        </View>
                    ))
                    : null
                }
                {this.props.loading
                    ? <ActivityIndicator />
                    : null
                }
                {this.props.error
                    ? <Text>{this.props.error.message}</Text>
                    : null
                }
            </View>
        );
    }
}

const mapStateToProps = (state) => ({
    posts: Object.values(state.Posts.List),
    error: state.Posts.error,
    loading: state.Posts.loading,
});
const mapDispatchToProps = (dispatch) => bindActionCreators(
    PostsResource.actionCreators,
    dispatch
);

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(TestClass);

How to handle the result of the requests

// postsReducer.js
import PostsResource from './resources';

export default = (
    state = {
        List: {},
        loading: false,
        error: null,
    },
    action
) => {
    switch (action.type) {
        case PostsResource.actions.CREATE:
        case PostsResource.actions.READ:
        case PostsResource.actions.UPDATE:
        case PostsResource.actions.DELETE:
        case PostsResource.actions.INDEX:
            return {
                ...state,
                loading: true,
            };
        case PostsResource.actions.CREATE_SUCCESS:
        case PostsResource.actions.READ_SUCCESS:
        case PostsResource.actions.UPDATE_SUCCESS:
            return {
                ...state,
                List: {
                    ...state.List,
                    [action.payload.id]: action.payload,
                },
                loading: false,
                error: null,
            };
        case PostsResource.actions.DELETE_SUCCESS:
            const updatedList = { ...state.List };

            delete updatedList[action.meta.id];

            return {
                ...state,
                List: updatedList,
                loading: false,
                error: null,
            };
        case PostsResource.actions.INDEX_SUCCESS:
            return {
                ...state,
                List: {
                    ...state.List,
                    ...action.payload.reduce((accumulator, post) => {
                        accumulator[post.id] = post;

                        return accumulator;
                    }, {}),
                },
                loading: false,
                error: null,
            };
        case PostsResource.actions.CREATE_FAILURE:
        case PostsResource.actions.READ_FAILURE:
        case PostsResource.actions.UPDATE_FAILURE:
        case PostsResource.actions.DELETE_FAILURE:
        case PostsResource.actions.INDEX_FAILURE:
            return {
                ...state,
                loading: false,
                error: action.payload,
            };
        default:
            return state;
    }
};

Options

KEY VALUE DEFAULT VALUE WHAT DOES IT DO
API_URL URL 'localhost/' The Base url of your API
CAMELIZE_DECAMELIZE true/false true All the keys of objects coming in from the server will be camelized (ex: customer_id will be transformed to customerId) and everything that goes out of the client will be decamelized (ex: customerId will be transformed to customer_id)
DEBUG true/false true console.logs stuff about what is happening network-wise
HTTP_ERROR_CODE_MESSAGES An object with keys being the status code and values being the messages {
401: 'FORBIDDEN',
403: 'FORBIDDEN',
404: 'NOT_FOUND',
}
Customize the messages of the errors caught while doing the requests
HTTP_HEADERS Headers put on the requests {} Headers to add to the requests
JWT_TOKEN YOUR_JWT_TOKEN null This will add an Authorization header with value Bearer YOUR_JWT_TOKEN
REDUX_ACTIONS_PREFIX Prefix to add to actions type 'RNRRR' Namespaces actions to avoid conflicts with your actions

TODO

  • postfix actionCreators with appropriate endpoint camelized and pluralized term
  • Create Basic Reducer automatically?

GitHub