React Boilerplate Redux Saga Hoc

Search Results

No results for 'N/A'
1. Getting Started
2. Basic Concepts
3. Handlers
4. Advanced Concepts
5. Examples6. Advanced Examples7. ParamsApi ReferenceComplete DocumentationPrevious DocumentationInstallation# Basic usage# Store Configuration# Why should i use this package# Benefits of using this package# Whether this package will support for react-native# Step by step process# storing data automatically by calling api# sending query parameters to the api# Callbacks for handling success or failure# Manually storing data by calling custom task# Getting Data from store# Updating data automatically by calling api# Updating Data in an Array# Callbacks for handling Updated Data# Manually updating data by calling custom task# Getting Updated Data from store# storing infinite data automatically by calling api# Prepend data instead of appending data# Callbacks for handling success or failure# Manually storing or updating infinite data by calling custom task# Getting Data from store# Deleting data by calling api# Deleting key in an object# Manually Deleting by calling custom task# Getting Data from store# Adding filters in api calls# Deleting key in an Filter object# Manually Deleting Filter Data by calling custom task# Getting Filter Data from store# Adding filters in Deep Object# Deleting key in an deep object# Manually Deleting Subkey Data by calling custom task# Getting Filter Data from store# Creating Custom Reducer# Modifying reducer constants# Don't reset on setting to initial state# cancel on unmount# Axios Interceptors# Inject saga and reducer to the store# Inject saga and reducer to the store by using hooks# Safe Function# Other Util functions# PARAMSContributingLicenseReadme
Note: If you using version 1.0.89 or below please refer this documentation

React Boilerplate Redux Saga HOC

Installation

This package requires React 16.8.4 or later.

Use the package manager npm to install react-boilerplate-redux-saga-hoc.

npm i react-boilerplate-redux-saga-hoc

or

yarn add react-boilerplate-redux-saga-hoc

# Basic usage

/** App.js **/
import React from "react";
import PropTypes from "prop-types";
import { Provider } from "react-redux";
import { compose } from "redux";
import {
HOC,
commonConstants,
store as configureStore,
} from "react-boilerplate-redux-saga-hoc";
import useCustomHook from "./hooks/customHook";
const initialState = {};
const isWeb = true; // default true
const store = configureStore(initialState, isWeb);
const AuthenticationHOC = HOC({
initialState: {
profile: {},
},
apiEndPoints: {
TEST_API: {},
REGISTER_API: {
url: `users/user-signup/`,
method: "POST",
},
},
name: "Auth",
});
const DashboardHOC = HOC({
initialState: {
profile: {},
},
apiEndPoints: {
TEST_SUB_API: {},
DASHBOARD_API: {
url: `users/user-signup/`,
method: "POST",
},
},
name: "Dash",
});
const CustomComponent = compose(
AuthenticationHOC,
DashboardHOC
)((props) => {
const hooks = useCustomHook(props, { commonConstants });
const {
test: { data: testData },
test_sub: { data: testSubData },
} = hooks;
console.log(props, hooks);
return <div>customHooks</div>;
});
export default function App(props) {
return (
<Provider store={store}>
<CustomComponent />
</Provider>
);
}
export default App;

# Store Configuration

Note: No need to configure store seperately.Store can be imported from react-boilerplate-redux-saga-hoc

import React from "react";
import { Provider } from "react-redux";
import { store as configureStore } from "react-boilerplate-redux-saga-hoc";
const initialState = {};
const isWeb = true;
const store = configureStore(initialState, isWeb); // by default second parameter will be true
export default function App(props) {
return (
<Provider store={store}>
<CustomComponent />
</Provider>
);
}
export default App;

Before Proceeding Further

We already knows redux is a valuable tool for organising your state and also redux-saga is a powerful middleware for handling side Effects.With the help of those two tools we have created a package for handling api calls and storing data in an organised way.

# Why should i use this package

Go to Top

Note: This package is not an alternative for redux and redux-saga

This package is mostly for developer who wants to make development faster and also to handle most of the api calls.

# Benefits of using this package

Go to Top

  • Handles api calls automatically by itself
  • No need to create store, constants, actions, saga, reducer
  • It handles cancelling api call by itself
  • Handles error, success, cancel, loading, infinite data handling
  • No worry about api calls, loaders...etc
  • No separate coding needed for react and react native

# Whether this package will support for react-native

Go to Top

Yes ,This package will support for both react and react-native

How does it works

# Step by step process

Go to Top

  1. Importing a package

    import {
    HOC,
    commonConstants,
    store,
    } from "react-boilerplate-redux-saga-hoc";
  2. Creating Api-End-Points

    import {
    HOC,
    commonConstants,
    store,
    } from "react-boilerplate-redux-saga-hoc";
    const BASE_URL = "https://jsonplaceholder.typicode.com";
    const DEMO_GET_API = {
    url: `${BASE_URL}/posts`,
    method: "GET",
    responseStatusCode: [900],
    responseStatusCodeKey: "code",
    responseDataKey: "data",
    responseMessageKey: "status",
    errorMessageKey: "error",
    };
    const DEMO_POST_API = {
    url: ({ id }) => `${BASE_URL}/posts/${id}`,
    method: "POST",
    responseStatusCode: [900],
    responseStatusCodeKey: "code",
    responseDataKey: "data",
    responseMessageKey: "status",
    errorMessageKey: "error",
    };
    const DEMO_DELETE_API = {
    url: ({ id }) => `${BASE_URL}/posts/${id}`,
    method: "DELETE",
    responseStatusCode: [900],
    responseStatusCodeKey: "code",
    responseDataKey: "data",
    responseMessageKey: "status",
    errorMessageKey: "error",
    };
    const DEMO_PUT_API = {
    url: ({ id, type }) => `${BASE_URL}/posts/${id}/${type}`,
    method: "PUT",
    responseStatusCode: [900],
    responseStatusCodeKey: "code",
    responseDataKey: "data",
    responseMessageKey: "status",
    errorMessageKey: "error",
    };

    Note: You can create a separate file for api-end-points.js.

  3. Creating HOC

    import {
    HOC,
    commonConstants,
    store,
    } from "react-boilerplate-redux-saga-hoc";
    import {
    DEMO_GET_API,
    DEMO_POST_API,
    DEMO_DELETE_API,
    DEMO_PUT_API,
    } from "./api-end-points.js";
    const Auth_HOC = HOC({
    initialState: {
    profile: {},
    },
    apiEndPoints: {
    DEMO_GET_API,
    DEMO_POST_API,
    DEMO_DELETE_API,
    DEMO_PUT_API,
    },
    name: "Auth",
    });

    Note: When you create HOC for every endpoints it will create constants, actions, reducer, saga for you..

    Great: We are almost done.next step is connect hoc with our component.

  4. Connecting with Component

    import {
    HOC,
    commonConstants,
    store,
    } from "react-boilerplate-redux-saga-hoc";
    import { compose } from "redux";
    import {
    DEMO_GET_API,
    DEMO_POST_API,
    DEMO_DELETE_API,
    DEMO_PUT_API,
    } from "./api-end-points.js";
    const AuthenticationHOC = HOC({
    initialState: {
    profile: {},
    },
    apiEndPoints: {
    DEMO_GET_API,
    DEMO_POST_API,
    DEMO_DELETE_API,
    DEMO_PUT_API,
    },
    name: "Auth",
    });
    const CustomComponent = compose(AuthenticationHOC)((props) => {
    const hooks = useCustomHook(props, { commonConstants }); // discuss later below
    const {
    get: { data },
    } = hooks;
    console.log(props, data);
    return <div>customHooks</div>;
    });
  5. Configuring Store

    import {
    HOC,
    commonConstants,
    store as configureStore,
    } from "react-boilerplate-redux-saga-hoc";
    const initialState = {};
    const isWeb = true;
    const store = configureStore(initialState, isWeb); // by default second parameter will be true
    export default function App(props) {
    return (
    <Provider store={store}>
    <CustomComponent />
    </Provider>
    );
    }
    export default App;
  6. Create custom hook

    import { useEffect, useMemo } from 'react';
    const initialLoaderState = true;
    export default (
    {
    Auth_hoc: {
    actions: {
    // For API CALLS
    DEMO_GET_API_CALL,
    DEMO_POST_API_CALL,
    DEMO_DELETE_API_CALL,
    DEMO_PUT_API_CALL,
    // For API CANCEL
    DEMO_GET_API_CANCEL,
    DEMO_POST_API_CANCEL,
    DEMO_DELETE_API_CANCEL,
    DEMO_PUT_API_CANCEL,
    // For Modifying reducer without api calls
    DEMO_GET_API_CUSTOM_TASK,
    DEMO_POST_API_CUSTOM_TASK,
    DEMO_DELETE_API_CUSTOM_TASK,
    DEMO_PUT_API_CUSTOM_TASK
    }
    },
    Auth_data: {
    // For getting data
    DEMO_GET_API,
    DEMO_POST_API,
    DEMO_DELETE_API,
    DEMO_PUT_API,
    },
    getData,
    dispatch,
    },
    { commonConstants: { ON_UNMOUNT } },
    ) => {
    const { ON_SUCCESS } = commonConstants;
    useEffect(() => {
    DEMO_GET_API_CALL({
    query: {
    skip: 0,
    limit: 1,
    },
    });
    DEMO_POST_API_CALL({
    payload: {
    name: 'name',
    age: 20,
    },
    params: {
    id: 1,
    },
    });
    DEMO_DELETE_API_CALL({
    params: {
    id: 1,
    },
    });
    DEMO_PUT_API_CALL({
    params: {
    id: 1,
    type: 'male',
    },
    });
    return () => {
    // For cancelling incomplete api call if you are unmounting this will avoid unwanted network traffic.This is optional no need to pass all the times
    DEMO_GET_API_CUSTOM_TASK(ON_UNMOUNT);
    DEMO_POST_API_CUSTOM_TASK(ON_UNMOUNT);
    DEMO_DELETE_API_CUSTOM_TASK(ON_UNMOUNT);
    DEMO_PUT_API_CUSTOM_TASK(ON_UNMOUNT);
    };
    }, []);
    DEMO_GET_API,
    DEMO_POST_API,
    DEMO_DELETE_API,
    DEMO_PUT_API,
    const get = useMemo(() => getData(DEMO_GET_API, {}, initialLoaderState), [DEMO_GET_API]);
    const post = useMemo(() => getData(DEMO_POST_API, {}, false), [
    DEMO_POST_API,
    ]);
    const put = useMemo(() => getData(DEMO_POST_API, {}, false), [
    DEMO_PUT_API,
    ]);
    const delete = useMemo(() => getData(DEMO_DELETE_API, {}, false), [
    DEMO_DELETE_API,
    ]);
    // const { loader , data , lastUpdated } = get;
    return {
    get,
    post,
    put,
    delete
    };
    };
  7. Using the hook

    import {
    HOC,
    commonConstants,
    store,
    } from "react-boilerplate-redux-saga-hoc";
    const CustomComponent = compose(AuthenticationHOC)((props) => {
    const hooks = useCustomHook(props, { commonConstants });
    const {
    get: { data },
    } = hooks;
    console.log(props, data);
    return <div>customHooks</div>;
    });

Note: This is the basic setup for handling api calls.You will find the more useful features in below such as updating, deleting data..Handling multiple tasks..etc

Storing Data

Go to Top

# storing data automatically by calling api

Note: Data will be stored automatically in the reducer and also it handles all the states..such as error,infinte,loader..etc

const BASE_URL = "https://example.com/";
const DEMO_API_CONFIGURATION = {
url: `${BASE_URL}user/`,
method: "GET",
responseStatusCode: [900],
responseStatusCodeKey: "code",
responseDataKey: "data",
responseMessageKey: "status",
errorMessageKey: "error",
};
DEMO_API_CONFIGURATION_CALL();

# sending query parameters to the api

Go to Top

Note: No need to worry about appending query..Its more simpler just pass the object in the query parameter that will append query in the url.And also passing params is simpler..

Note: If you need to pass params in the url..then you have to change the url to function to receive params..just like give below..

const BASE_URL = "https://example.com/";
const DEMO_API_CONFIGURATION = {
url: ({ id }) => `${BASE_URL}user/${id}`,
method: "GET",
responseStatusCode: [900],
responseStatusCodeKey: "code",
responseDataKey: "data",
responseMessageKey: "status",
errorMessageKey: "error",
};
DEMO_API_CONFIGURATION_CALL({
params: { id: 1 },
query: { skip: 10, age: [20, 20] },
payload: { age: 20 }, // for post calls
paramsSerializer: { arrayFormat: "bracket" }, //default: none - refer query-string npm package
});

# Callbacks for handling success or failure

Go to Top

Note: Callback are another helper function which handles errors, success, cancel..etc

const BASE_URL = 'https://example.com/'
const DEMO_API_CONFIGURATION = {
url: `${BASE_URL}user/${id}`,
method: 'GET',
responseStatusCode: [900],
responseStatusCodeKey: 'code',
responseDataKey: 'data',
responseMessageKey: 'status',
errorMessageKey: 'error',
};
const responseErrorParser = data =>
(Array.isArray(data) &&
data.reduce((acc, curr) => {
const [key, message] = Object.entries(curr)[0];
const payloadKey = key.split(',')[1];
return {
...acc,
[payloadKey]: message,
};
}, {})) ||
{};
DEMO_API_CONFIGURATION_CALL({
params: { id: 1 },
query: { skip: 10,age: [20,20] },
paramsSerializer: {arrayFormat: 'bracket'} //default: none - refer query-string npm package
successCallback: ({ res, data, message, status }) => {
// handle toast or call any other api
},
errorCallback: ({ error, errorData: responseErrorParser, message, status, errors }) => {
// handle toast or call any other api
}
});

# Manually storing data by calling custom task

Go to Top

Note: Don't worry about terms Manually,Automatically , its just the common word we used in the real world..The way how it stores or handles data i specified as manually, automatically thats it..You will get used to it.

Automatically - It will handle api calls and stores data and also handles all the errors, success, loaders...etc

Manually - It will slight different instead of api calls we are manually storing or updating data..This util function will help you update data in particular reducer..

import { HOC, commonConstants, store } from "react-boilerplate-redux-saga-hoc";
const { ON_SUCCESS } = commonConstants;
const { DEMO_API_CONFIGURATION_CUSTOM_TASK } = props;
DEMO_API_CONFIGURATION_CUSTOM_TASK(
ON_SUCCESS,
{},
{
data: {
name: "example",
},
}
);

# Getting Data from store

Go to Top

Note: We are almost done with basic setup, api calls, storing data...etcNow the main things we have to retrieve the data from the reducer..Don't Worry that is very much simpler than other task.

getData: Its an util function which gets the data from the particular reducer and also formats the data in order..(DEMO_API_CONFIGURATION, default || typeof data, initial-loader-state, filter)

const { getData, DEMO_API_CONFIGURATION } = props;
const test = useMemo(() => getData(DEMO_API_CONFIGURATION, {}, false), [
DEMO_API_CONFIGURATION,
]);
/**
test returns
{
loader,
data,
latUpdated,
isInfinite,
isInfiniteEnd
}
**/

Updating Data

# Updating data automatically by calling api

Go to Top

Note: It will only update if the reponse data is object otherwise it will replace the data.It wont update Array.Array will be handle diiferent way.you will find Below....

const BASE_URL = "https://example.com/";
const DEMO_API_CONFIGURATION = {
url: `${BASE_URL}user/`,
method: "GET",
responseStatusCode: [900],
responseStatusCodeKey: "code",
responseDataKey: "data",
responseMessageKey: "status",
errorMessageKey: "error",
};
DEMO_API_CONFIGURATION_CALL({
isUpdate: true,
});
Example: storedData = {
name: "example",
date: "22-8-2222",
};
responseData = {
name: "example 2",
};
UpdateData = {
name: "example 2",
date: "22-8-2222",
};

# Updating Data in an Array

Go to Top

Note: If the update data is Array it requires extra 2 parameters to itentify the Object thts is id,key

id - Array || string || number - Array of ids
key - Key example: 'user_id' <String>
const BASE_URL = 'https://example.com/'
const DEMO_API_CONFIGURATION = {
url: `${BASE_URL}user/${id}`,
method: 'GET',
responseStatusCode: [900],
responseStatusCodeKey: 'code',
responseDataKey: 'data',
responseMessageKey: 'status',
errorMessageKey: 'error',
};
DEMO_API_CONFIGURATION_CALL({
params: { id: 1 },
query: { skip: 10,age: [20,20] },
payload: { age: 20 }, // for post calls
paramsSerializer: {arrayFormat: 'bracket'} //default: none - refer query-string npm package
isUpdate: true,
key: 'id',
id: [ 2,3 ]
});
Example:
storedData = [{
id: 1
name: 'example',
date: '22-8-2222'
},
{
id: 2
name: 'example',
date: '22-8-2222'
}]
responseData = {
name: 'example 2'
}
UpdateData = [{
id: 1
name: 'example',
date: '22-8-2222'
},
{
id: 2
name: 'example 2',
date: '22-8-2222'
}]

Note: In the above scenario id number 3 will be ignored.If u want to append or prepend data there are some other ways...you will find below

# Callbacks for handling Updated Data

Go to Top

Note: There are several callback are available can be used in different requirements

const BASE_URL = 'https://example.com/'
const DEMO_API_CONFIGURATION = {
url: `${BASE_URL}user/${id}`,
method: 'GET',
responseStatusCode: [900],
responseStatusCodeKey: 'code',
responseDataKey: 'data',
responseMessageKey: 'status',
errorMessageKey: 'error',
};
DEMO_API_CONFIGURATION_CALL({
params: { id: 1 },
query: { skip: 10,age: [20,21] },
paramsSerializer: {arrayFormat: 'bracket'} //default: none - refer query-string npm package
updateCallback: (storeData: oldData,responseData: newData) => {
return {...oldData,...newData} || oldData.concat(newData) // It will update the data in paricular reducer
},
updateStateCallback: ({state: updatedState,data: responseData}) => {
return updatedState // By default it takes the updated state if returns undefined or null
},
errorCallback: ({ error, errorData: responseErrorParser, message, status, errors }) => {
// handle toast or call any other api
}
});

# Manually updating data by calling custom task

Go to Top

import { HOC, commonConstants, store } from "react-boilerplate-redux-saga-hoc";
const { ON_SUCCESS } = commonConstants;
const { DEMO_API_CONFIGURATION_CUSTOM_TASK } = props;
DEMO_API_CONFIGURATION_CUSTOM_TASK(
ON_SUCCESS,
{
isUpdate: true,
key: "id",
id: [1],
},
{
data: {
name: "example",
},
}
);

# Getting Updated Data from store

const { getData, DEMO_API_CONFIGURATION } = props;
const test = useMemo(() => getData(DEMO_API_CONFIGURATION, {}, false), [
DEMO_API_CONFIGURATION,
]);
/**
test returns
{
loader,
data,
latUpdated,
isInfinite,
isInfiniteEnd
}
**/

Handling Infinite Data or Infinite Scrolling

Go to Top

# storing infinite data automatically by calling api

Note: Data will be stored automatically in the reducer and also it handles all the states..such as error,infinte,loader..etc

const BASE_URL = "https://example.com/";
const DEMO_API_CONFIGURATION = {
url: `${BASE_URL}user/`,
method: "GET",
responseStatusCode: [900],
responseStatusCodeKey: "code",
responseDataKey: "data",
responseMessageKey: "status",
errorMessageKey: "error",
};
DEMO_API_CONFIGURATION_CALL({
isInfinite: true,
});

Note: It will append data if already data is array...other wise it will replace the new data

# Prepend data instead of appending data

Note: It will store the data at the top instead of last

const BASE_URL = "https://example.com/";
const DEMO_API_CONFIGURATION = {
url: ({ id }) => `${BASE_URL}user/${id}`,
method: "GET",
responseStatusCode: [900],
responseStatusCodeKey: "code",
responseDataKey: "data",
responseMessageKey: "status",
errorMessageKey: "error",
};
DEMO_API_CONFIGURATION_CALL({
params: { id: 1 },
isInfinite: true,
query: { skip: 10, age: [20, 20] },
payload: { age: 20 }, // for post calls
isAppendTop: true,
paramsSerializer: { arrayFormat: "bracket" }, //default: none - refer query-string npm package
});

# Callbacks for handling success or failure

Go to Top

Note: Callback are another helper function which handles errors, success, cancel..etc

const BASE_URL = 'https://example.com/'
const DEMO_API_CONFIGURATION = {
url: `${BASE_URL}user/${id}`,
method: 'GET',
responseStatusCode: [900],
responseStatusCodeKey: 'code',
responseDataKey: 'data',
responseMessageKey: 'status',
errorMessageKey: 'error',
};
const responseErrorParser = data =>
(Array.isArray(data) &&
data.reduce((acc, curr) => {
const [key, message] = Object.entries(curr)[0];
const payloadKey = key.split(',')[1];
return {
...acc,
[payloadKey]: message,
};
}, {})) ||
{};
DEMO_API_CONFIGURATION_CALL({
params: { id: 1 },
isInfinite: true,
query: { skip: 10,age: [20,20] },
paramsSerializer: {arrayFormat: 'bracket'} //default: none - refer query-string npm package
successCallback: ({ res, data, message, status }) => {
// handle toast or call any other api
},
errorCallback: ({ error, errorData: responseErrorParser, message, status, errors }) => {
// handle toast or call any other api
}
});

# Manually storing or updating infinite data by calling custom task

Go to Top

Note: Calling custom task will allow us to manipulate data without calling any api..such as adding count,linit,skip..etc

import { HOC, commonConstants, store } from "react-boilerplate-redux-saga-hoc";
const { ON_SUCCESS } = commonConstants;
const { DEMO_API_CONFIGURATION_CUSTOM_TASK } = props;
DEMO_API_CONFIGURATION_CUSTOM_TASK(
ON_SUCCESS,
{
isInfinite: true,
isAppendTop: true, // by default it will append at the bottom
},
{
data: [
{
name: "example",
},
],
}
);

# Getting Data from store

Go to Top

Note: We are almost done with basic setup, api calls, storing data...etcNow the main things we have to retrieve the data from the reducer..Don't Worry that is very much simpler than other task.

getData: Its an util function which gets the data from the particular reducer and also formats the data in order..(DEMO_API_CONFIGURATION, default || typeof data, initial-loader-state, filter)

const { getData, DEMO_API_CONFIGURATION } = props;
const test = useMemo(() => getData(DEMO_API_CONFIGURATION, [], false), [
DEMO_API_CONFIGURATION,
]);
/**
test returns
{
loader,
data,
latUpdated,
isInfinite,
isInfiniteEnd
}
**/

Deleting Data

Go to Top

# Deleting data by calling api

Note: Data will be deleted automatically in the reducer and also it handles all the states..such as error,infinte,loader..etc

const BASE_URL = 'https://example.com/';
const DEMO_API_CONFIGURATION = {
url: ({ id }) => `${BASE_URL}user/${id}`,
method: 'GET',
responseStatusCode: [900],
responseStatusCodeKey: 'code',
responseDataKey: 'data',
responseMessageKey: 'status',
errorMessageKey: 'error',
};
DEMO_API_CONFIGURATION_CALL({
params: { id: 2 },
isDelete: true,
key: 'id'.
id: [ 2 ]
});

Note: It will remove that particular object in an array

# Deleting key in an object

Go to Top

Note: It is almost similar to update, but instead of updating particular key it will delete the particular key from the object

const BASE_URL = 'https://example.com/';
const DEMO_API_CONFIGURATION = {
url: ({ id }) => `${BASE_URL}user/${id}`,
method: 'GET',
responseStatusCode: [900],
responseStatusCodeKey: 'code',
responseDataKey: 'data',
responseMessageKey: 'status',
errorMessageKey: 'error',
};
DEMO_API_CONFIGURATION_CALL({
params: { id: 1 },
isInfinite: true,
query: { skip: 10, age: [20, 20] },
payload: { age: 20 }, // for post calls
isAppendTop: true,
paramsSerializer: { arrayFormat: 'bracket' }, //default: none - refer query-string npm package
isDeleteKey: true,
id: [ 1,2,3 ],
key: 'id',
deleteKey: [ 'name','age']
});
Example:
data = [ { id: 1, name: 'name1',age: '13',gender: 'male' },{ id: 1, name: 'name1',age: '12',gender: 'female' }]
After executing task:
data = [ { id: 1, gender: 'male' },{ id: 2, gender: 'female' }]

# Manually Deleting by calling custom task

Note: Calling custom task will allow us to manipulate data without calling any api..such as adding count,linit,skip..etc

import { HOC, commonConstants, store } from "react-boilerplate-redux-saga-hoc";
const { ON_SUCCESS } = commonConstants;
const { DEMO_API_CONFIGURATION_CUSTOM_TASK } = props;
DEMO_API_CONFIGURATION_CUSTOM_TASK(ON_SUCCESS, {
isDelete: true,
id: [1, 2],
key: "id",
});

# Getting Data from store

Go to Top

getData: Its an util function which gets the data from the particular reducer and also formats the data in order..(DEMO_API_CONFIGURATION, default || typeof data, initial-loader-state, filter)

const { getData, DEMO_API_CONFIGURATION } = props;
const test = useMemo(() => getData(DEMO_API_CONFIGURATION, [], false), [
DEMO_API_CONFIGURATION,
]);
/**
test returns
{
loader,
data,
latUpdated,
isInfinite,
isInfiniteEnd
}
**/

Adding Filters

# Adding filters in api calls

Go to Top

Note: Suppose there is a scenario where same api is used with multiple filters.In that case we have to Store different data.

const BASE_URL = 'https://example.com/';
const DEMO_API_CONFIGURATION = {
url: ({ id }) => `${BASE_URL}user/${id}`,
method: 'GET',
responseStatusCode: [900],
responseStatusCodeKey: 'code',
responseDataKey: 'data',
responseMessageKey: 'status',
errorMessageKey: 'error',
};
DEMO_API_CONFIGURATION_CALL({
params: { id: 2 },
query: { type: 'name' }
filter: [ 'name' ],
});
Example:
responseData = { id: 1 }
storeData = { name: { id: 1 }

Note: In this case it will store the data in an object with key 'name'

# Deleting key in an Filter object

Go to Top

Note: It is almost similar to update, but instead of updating particular key it will delete the particular key from the object

const BASE_URL = 'https://example.com/';
const DEMO_API_CONFIGURATION = {
url: ({ id }) => `${BASE_URL}user/${id}`,
method: 'GET',
responseStatusCode: [900],
responseStatusCodeKey: 'code',
responseDataKey: 'data',
responseMessageKey: 'status',
errorMessageKey: 'error',
};
DEMO_API_CONFIGURATION_CALL({
params: { id: 1 },
isInfinite: true,
query: { skip: 10, age: [20, 20] },
payload: { age: 20 }, // for post calls
isAppendTop: true,
paramsSerializer: { arrayFormat: 'bracket' }, //default: none - refer query-string npm package
isDeleteKey: true,
id: [ 1,2,3 ],
key: 'id',
deleteKey: [ 'name','age'],
filter: [ 'name' ],
});
Example:
data = { name : [
{ id: 1, name: 'name1',age: '13',gender: 'male' },
{ id: 1, name: 'name1',age: '12',gender: 'female' }
]}
After executing task:
data = { name : [
{ id: 1, gender: 'male' },
{ id: 2, gender: 'female' }
}]

# Manually Deleting Filter Data by calling custom task

Go to Top

Note: Calling custom task will allow us to manipulate data without calling any api..such as adding count,limit,skip..etc

import { HOC, commonConstants, store } from "react-boilerplate-redux-saga-hoc";
const { ON_SUCCESS } = commonConstants;
const { DEMO_API_CONFIGURATION_CUSTOM_TASK } = props;
DEMO_API_CONFIGURATION_CUSTOM_TASK(ON_SUCCESS, {
isDelete: true,
id: [1, 2],
key: "id",
filter: ["name"],
});

# Getting Filter Data from store

Go to Top

getData: Its an util function which gets the data from the particular reducer and also formats the data in order..(DEMO_API_CONFIGURATION, default || typeof data, initial-loader-state, filter)

const { getData, DEMO_API_CONFIGURATION } = props;
const nameData = useMemo(
() => getData(DEMO_API_CONFIGURATION, [], false, ["name"]),
[DEMO_API_CONFIGURATION]
);
const ageData = useMemo(
() => getData(DEMO_API_CONFIGURATION, [], false, ["age"]),
[DEMO_API_CONFIGURATION]
);
/**
test returns
{
loader,
data,
latUpdated,
isInfinite,
isInfiniteEnd
}
**/

Using Subkey for accessing deep object

Go to Top

# Adding filters in Deep Object

Note: Incase you want to update deep object we can use subkey to navigate to that object

const BASE_URL = 'https://example.com/';
const DEMO_API_CONFIGURATION = {
url: ({ id }) => `${BASE_URL}user/${id}`,
method: 'GET',
responseStatusCode: [900],
responseStatusCodeKey: 'code',
responseDataKey: 'data',
responseMessageKey: 'status',
errorMessageKey: 'error',
};
DEMO_API_CONFIGURATION_CALL({
subKey: [ 'data' ],
params: { id: 2 },
query: { type: 'name' }
filter: [ 'name' ],
});
Example:
responseData = { data : { id: 1 } }
storeData = { name: { data: { id: 1 } }

Note: In this case it will store the data in an object with key 'name'

# Deleting key in an deep object

Go to Top

Note: It is almost similar to update, but instead of updating particular key it will delete the particular key from the object

const BASE_URL = 'https://example.com/';
const DEMO_API_CONFIGURATION = {
url: ({ id }) => `${BASE_URL}user/${id}`,
method: 'GET',
responseStatusCode: [900],
responseStatusCodeKey: 'code',
responseDataKey: 'data',
responseMessageKey: 'status',
errorMessageKey: 'error',
};
DEMO_API_CONFIGURATION_CALL({
params: { id: 1 },
subKey: [ 'data' ],
isInfinite: true,
query: { skip: 10, age: [20, 20] },
payload: { age: 20 }, // for post calls
isAppendTop: true,
paramsSerializer: { arrayFormat: 'bracket' }, //default: none - refer query-string npm package
isDeleteKey: true,
id: [ 1,2,3 ],
key: 'id',
deleteKey: [ 'name','age'],
filter: [ 'name' ],
});
Example:
data = { name : [
{ id: 1, name: 'name1',age: '13',gender: 'male' },
{ id: 1, name: 'name1',age: '12',gender: 'female' }
]}
After executing task:
data = { name : { data : [
{ id: 1, gender: 'male' },
{ id: 2, gender: 'female' }
]}}

# Manually Deleting Subkey Data by calling custom task

Go to Top

Note: Calling custom task will allow us to manipulate data without calling any api..such as adding count,limit,skip..etc

import { HOC, commonConstants, store } from "react-boilerplate-redux-saga-hoc";
const { ON_SUCCESS } = commonConstants;
const { DEMO_API_CONFIGURATION_CUSTOM_TASK } = props;
DEMO_API_CONFIGURATION_CUSTOM_TASK(ON_SUCCESS, {
isDelete: true,
subKey: ["data"],
id: [1, 2],
key: "id",
filter: ["name"],
});

# Getting Filter Data from store

Go to Top

getData: Its an util function which gets the data from the particular reducer and also formats the data in order..(DEMO_API_CONFIGURATION, default || typeof data, initial-loader-state, filter)

const { getData, DEMO_API_CONFIGURATION } = props;
const nameData = useMemo(
() => getData(DEMO_API_CONFIGURATION, [], false, ["name"]),
[DEMO_API_CONFIGURATION]
);
/**
test returns
{
loader,
data,
latUpdated,
isInfinite,
isInfiniteEnd
}
**/

Advanced Topics

# Creating Custom Reducer

Go to Top

import { HOC, commonConstants, store } from "react-boilerplate-redux-saga-hoc";
const AuthenticationHOC = HOC({
initialState: {
profile: {},
},
apiEndPoints: {
TEST_API: {},
REGISTER_API: {
url: `users/user-signup/`,
method: "POST",
},
},
constantReducer: ({ type, state, action, constants, initialState }) => {
if (type === "LOGOUT") return initialState;
return state;
},
name: "Auth",
});
Example: const { dispatch } = props;
dispatch({ type: "LOGOUT" });

# Modifying reducer constants

Go to Top

import { HOC, commonConstants, store } from "react-boilerplate-redux-saga-hoc";
const { CALL, ON_SUCCESS } = commonConstants;
const reducer = ({
constants,
successData,
restSuccessData,
payload,
query,
params,
restPayload,
loadingStatus,
statusCode,
type,
method,
statusMessage,
errorData,
restErrorData,
ResetState,
InitialState,
commonHandler,
defaultReducerHandler,
}) => {
switch (type) {
case "RESET":
switch (method) {
case ON_SUCCESS:
return isMobileApp
? newObject(InitialState)
: newObject(state, ResetState);
default:
return state;
}
case authenticationConstants.VERIFY_OTP_API[CALL]:
switch (method) {
case ON_SUCCESS:
return newState(({ [type]: Data }) => ({
profile: successData,
isLoggedIn: !!successData.data.mobile_number,
[type]: newObject(Data, {
lastUpdated: generateTimeStamp(),
data: successData,
}),
}));
default:
return state;
}
default:
return defaultReducerHandler();
}
};
const AuthenticationHOC = HOC({
initialState: {
profile: {},
},
apiEndPoints: {
TEST_API: {},
VERIFY_OTP_API: {
url: `users/verify-otp/`,
method: "POST",
},
REGISTER_API: {
url: `users/user-signup/`,
method: "POST",
},
},
reducer,
constantReducer: ({ type, state, action, constants, initialState }) => {
if (type === "LOGOUT") return initialState;
return state;
},
name: "Auth",
});

# Don't reset on setting to initial state

Go to Top

import { HOC, commonConstants, store } from 'react-boilerplate-redux-saga-hoc';
const TEST_API = {};
const REGISTER_API = {
url: `users/user-signup/`,
method: 'POST',
}
const AuthenticationHOC = HOC({
initialState: {
profile: {},
},
dontReset: {
TEST_API,REGISTER_API
}
apiEndPoints: {
TEST_API,
REGISTER_API,
},
constantReducer: ({ type, state, action, constants, initialState }) => {
if (type === 'LOGOUT')
return initialState;
return state;
},
name: 'Auth',
});
Example:
const { dispatch } = props;
dispatch({ type: 'RESET' });

Cancelling Api Calls

# cancel on unmount

Go to Top

Note: This function will help cancel unwanted api calls

const { ON_SUCCESS, ON_UNMOUNT } = commonConstants;
const { DEMO_API_CONFIGURATION_CANCEL, DEMO_API_CONFIGURATION_CALL } = props;
useEffect(() => {
DEMO_API_CONFIGURATION_CALL();
return () => {
DEMO_API_CONFIGURATION_CANCEL(ON_UNMOUNT);
};
}, []);

Note: ON_UMOUNT will reset the particular state to initial

# Axios Interceptors

Go to Top

import { HOC, commonConstants, store } from 'react-boilerplate-redux-saga-hoc';
mport axios from 'axios';
import promise from 'promise';
const request = axios;
request.defaults.withCredentials = true;
request.interceptors.request.use(
config => {
if (!config.headers.Authorization) {
const token = localStorage.getItem('token');
if (token)
request.defaults.headers.common.Authorization = `Bearer ${token}`;
}
return config;
},
error => promise.reject(error),
);
request.interceptors.response.use(
response => {
return response;
},
error => Promise.reject(error),
);
const AuthenticationHOC = HOC({
initialState: {
profile: {},
},
apiEndPoints: {
TEST_API: {},
REGISTER_API: {
url: `users/user-signup/`,
method: 'POST',
},
},
axiosInterceptors: request,
constantReducer: ({ type, state, action, constants, initialState }) => {
if (type === 'LOGOUT')
return initialState;
return state;
},
name: 'Auth',
});
Example:
const { dispatch } = props;
dispatch({ type: 'LOGOUT' });

# Inject saga and reducer to the store

Go to Top

Note: By injecting reducer and saga you can able to create your own reducer and saga.It helps in most of the scnarios such as polling,creating youer own logic ..etc

import { takeLatest } from 'redux-saga/effects';
import { injectReducer,injectSaga } from 'react-boilerplate-redux-saga-hoc';
const reducer = (state,action) => {
return state;
}
function* getRepos() {
}
const saga = function* () {
yield takeLatest('LOAD_DATA', getRepos);
}
const withSaga = injectSaga({'dashboard',saga})
const withReducer = injectReducer({'dashboard',reducer})
export default compose(
withSaga,
withReducer,
)(Dashboard);

# Inject saga and reducer to the store by using hooks

Go to Top

Note: By injecting reducer and saga you can able to create your own reducer and saga. It helps in most of the scenarios such as polling,creating your own logic..etc

import { takeLatest } from "redux-saga/effects";
import {
useInjectReducer,
useInjectSaga,
} from "react-boilerplate-redux-saga-hoc";
const reducer = (state, action) => {
return state;
};
function* getRepos() {}
const saga = function*() {
yield takeLatest("LOAD_DATA", getRepos);
};
const key = "Dashboard";
const Dashboard = () => {
useInjectReducer({ key, reducer });
useInjectSaga({ key, saga });
return <div />;
};
export default Dashboard;

Util Functions

# Safe Function

Go to Top

Note: This function will be used for accessing deep level of object.

Example 1:
const defaultValue = '';
const object = { name: { name: { age: { person: 'jfjj' } } } }
const Person = Safe(object , '.name.name.age.person', defaultValue);
Example 2:
const callback = (person) => return person + person;
const Person = Safe(object , '.name.name.age.person', defaultValue , callback)

Note: defaultValue is typeof value should return ,if the value return array by default safe function will return default value...if don't pass any value in third parameter it return the ececuted value otherwise return null.

# Other Util functions

Go to Top

import { cloneObject , newObject } from 'react-boilerplate-redux-saga-hoc';
Example 1:
const oldObject = { name: '' };
const newObject = { age: '' };
const obj = cloneObject(oldObject, newObject)
obj returns { name: '', age: '' }
Example 2:
const object = { name: '',profile: { name : '' } };
const obj = newObject(object,({ profile }) => ({
profile: newObject(profile, { age: 20 })
}))
obj returns { name: '', age: 20 }

# PARAMS

Go to Top

isUpdate - update an object or array <Boolean>
isUpdateKey -<Boolean> updating or adding a key in existing object
updateIdCallback - <Function>
updateStateCallback - <Function>
subKey - <Array> accessing deep object
isInfinite - storing infinite data <Boolean>
isDelete - deleting a data in an array <Boolean> takes id, key as an extra
parameter
isDeleteKey - delete a key in an object <Boolean>
deleteKey - <Array> of keys to delete
filter - <Array> storing data based on filter
filter - <Array> update n number of filters at single task
eg: [['filter1'],['filter2']]
id - <Array> || <string> || <number>
key - id Key example: 'user_id' <String>
clearData - clearing data <Boolean> for infinite
isAppendTop - for storing data in top for infinite
limit - <Number> for infinite
query - <Object> adding query parameters to url
params - <Object> params for aps eg:api/:id
successCallback - <Function> <{res : <Object>,data<Object>,status, message}>
errorCallback - <Function> <{error : <Error instance>,errorData<Object>,status, message}>
updateCallback - <Function> <Object>
payload - <Function>
ON_SUCCESS - storing data based on success data or loaded data
ON_UNMOUNT - reset the data
ON_ERROR - setting the loader false and error data
Api End Point Params -
* url,
* method,
* responseMessageKey,
* responseDataKey,
* responseStatusCodeKey,
* responseStatusCodeKey,
* errorStatusKey,
* errorMessageKey,
* errorDataKey,
* errorHandlerStatusCode,
* effect - takeLatest default || 'every' for takeEvery
GetData - returns
* data - <Object>
* loader - <Boolean>
* lastUpdated - <Boolean>
* isInfinite - <Boolean>
* isInfiniteEnd - <Boolean>
GetData - params
* Object(data) - <Object>
* defaultValue - <Array || Object || Boolean || null || String >
* loader - <Boolean>
* filter - <Array>

# Handling Multiple tasks

Go to Top

// hooks/customHook.js
import { useEffect, useMemo } from 'react';
export default (
{
TEST_API_CUSTOM_TASK,
TEST_SUB_API_CUSTOM_TASK,
Auth: { TEST_API },
Dash: { TEST_SUB_API }
getData,
},
{ commonConstants },
) => {
const { ON_SUCCESS } = commonConstants;
useEffect(() => {
TEST_SUB_API_CUSTOM_TASK(
ON_SUCCESS,
{},
{
data: {
count: 20,
items: [
{
name: 'cartoon',
},
],
},
},
);
TEST_SUB_API_CUSTOM_TASK(
ON_SUCCESS,
{},
{
data: {
count: 20,
items: [
{
name: 'cartoon',
},
],
},
},
);
setTimeout(() => {
TEST_SUB_API_CUSTOM_TASK(
ON_SUCCESS,
{
subKey: ['items'],
isInfinite: true,
},
{
data: {
count: 50,
items: [
{
name: 'mango',
},
],
},
},
);
}, 1);
setTimeout(() => {
TEST_SUB_API_CUSTOM_TASK(
ON_SUCCESS,
{
subKey: ['items'],
isDelete: true,
id: 'cartoon',
key: 'name',
},
{},
);
}, 2);
setTimeout(() => {
TEST_SUB_API_CUSTOM_TASK(
ON_SUCCESS,
{
subKey: ['items'],
isDeleteKey: true,
id: 'mango',
key: 'name',
deleteKey: ['name'],
},
{},
);
}, 3);
setTimeout(() => {
TEST_SUB_API_CUSTOM_TASK(
ON_SUCCESS,
{
// subKey: ['items'],
// isInfinite: true,
},
{
data: {
count: 100,
items: [
{
name: '1',
},
{
name: '2',
},
{
name: '3',
},
{
name: '4',
},
],
},
},
);
}, 4);
setTimeout(() => {
TEST_SUB_API_CUSTOM_TASK(ON_SUCCESS, {
tasks: [
{
task: 'add',
params: {
clearData: true,
isInfinite: true,
filter: ['check'],
},
value: {
count: 30,
items: [
{ name: '10' },
{ name: '15' },
{ name: '25' },
{ name: '35' },
{ name: '45' },
],
},
},
{
task: 'isDelete',
params: {
subKey: ['items'],
id: '1',
key: 'name',
filter: ['check'],
},
value: {},
},
{
task: 'append',
params: {
subKey: ['items'],
isInfinite: true,
// clearData: true,
filter: ['check'],
},
value: {
count: 300,
items: [{ name: '54' }],
data: [{ name: '54' }],
},
},
{
task: 'isDelete',
params: {
subKey: ['items'],
id: ['3', '4', '5'],
key: 'name',
filter: ['check'],
},
value: {},
},
{
task: 'isUpdate',
params: {
subKey: ['items'],
id: ['10', '25', '15'],
key: 'name',
filter: ['check'],
values: {
'10': {
status: 'married',
},
'25': {
status: 'unmarried',
},
'15': {
status: 'married',
},
},
},
value: {
count: 30,
limit: 20,
// items: { age: 30 },
},
},
{
task: 'isDeleteKey',
params: {
subKey: ['items'],
id: ['10'],
key: 'name',
filter: ['check'],
deleteKey: ['name'],
},
value: {},
},
{
task: 'isToggleKey',
params: {
subKey: ['items'],
id: ['15'],
key: 'name',
filter: ['check'],
toggleKey: ['name', 'status'],
},
value: {},
},
],
});
TEST_API_CUSTOM_TASK(ON_SUCCESS, {
tasks: [
{
task: 'add',
params: {
clearData: true,
isInfinite: true,
filter: ['check'],
},
value: [
{ name: '10' },
{ name: '15' },
{ name: '25' },
{ name: '35' },
{ name: '45' },
],
},
{
task: 'isDelete',
params: {
// subKey: ['items'],
id: '1',
key: 'name',
filter: ['check'],
},
value: {},
},
{
task: 'append',
params: {
// subKey: ['items'],
isInfinite: true,
filter: ['check'],
},
value: [{ name: '5' }],
},
{
task: 'append',
params: {
// subKey: ['items'],
isInfinite: true,
filter: ['check'],
// clearData: true,
},
value: [{ name: '54' }],
},
{
task: 'isDelete',
params: {
// subKey: ['items'],
id: ['3', '4'],
key: 'name',
filter: ['check'],
},
value: {},
},
{
task: 'isUpdate',
params: {
// subKey: ['items'],
id: ['10', '25', '15'],
key: 'name',
filter: ['check'],
values: {
'10': {
status: 'married',
},
'25': {
status: 'unmarried',
},
'15': {
status: 'married',
},
},
},
value: {
// items: { age: 30 },
},
},
{
task: 'isDeleteKey',
params: {
// subKey: ['items'],
id: ['10'],
key: 'name',
filter: ['check'],
deleteKey: ['status'],
},
value: {},
},
{
task: 'isToggleKey',
params: {
id: ['25'],
key: 'name',
filter: ['check'],
toggleKey: ['name', 'status'],
},
value: {},
},
],
});
}, 5);
}, []);
const test = useMemo(() => getData(TEST_API, {}, false), [TEST_API]);
const test_sub = useMemo(() => getData(TEST_SUB_API, {}, false), [
TEST_SUB_API,
]);
return {
test,
test_sub,
};
};

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

License

MIT