Class

ReduxApi

ReduxApi()

Redux Hooks. Instance name: reduxApi

You can use these hooks to customize the redux related configurations of your app such as adding a new epic, new middleware to store initialization, and more.

Constructor

# new ReduxApi()

Example
externalCodeSetup.reduxApi.METHOD_NAME

Methods

# addEpic(name, epic)

It adds a new epic to the root epic. Note: The new epic will replace the old epic if “name” used to add an epic already exists.

Parameters:
Name Type Description
name String

Epic name

epic Epic
Example

Call an epic

...

import { asObservable } from "@src/epics/rxUtils";
const { getApi } = require("@src/services");
import { errorToAction } from "@src/utils";
export const applyCustomCode = externalCodeSetup => {

const courseRemoveFail = (error) => ({
 type: "COURSE_REMOVE_ERROR",
 error,
})
 const courseRemoveItem = (
   action$,
   store
 ) =>
   action$
     .filter(
       a =>
         a.type === "COURSE_REMOVE_ITEM" //Observer for type "COURSE_REMOVE_ITEM". If called, proceed with mergeMap function
     )
     .mergeMap(a => {
       let state = store.getState();
       let { config } = state;

       //Create a "loading" observable
       const loadingObservable = Observable.of({
         type: "COURSE_REMOVE_LOADING"
       });

       const api = getApi(config);

       const urlPath = "https://api-to-call.com",
         method = "GET",
         paramsOrPayload = {},
         validation = {},
         isfullUrl = true,
         headers = {
           appid: config.app_id
         }

       //Use BuddyBoss helper function "api.customRequest"
       const apiToCall = api
         .customRequest(
           urlPath,
           method,
           paramsOrPayload,
           validation,
           headers,
           isfullUrl
         );

       const requestObservable = asObservable(apiToCall)
         .map(() => ({
           type: "COURSE_REMOVE_SUCCESS"
          }))
         .catch(ex =>
           Observable.of(
            errorToAction(ex, e =>
             courseRemoveFail(e)
           )));
       return Observable.concat(loadingObservable, requestObservable);

     });

 externalCodeSetup.reduxApi.addEpic("courseRemoveItem", courseRemoveItem);
}

# addMiddleware(middleware)

You can use it to add a new middleware to store initialization

Parameters:
Name Type Description
middleware ReduxMiddleware
Example
import thunk from 'redux-thunk';
...
externalCodeSetup.reduxApi.addMiddleware(thunk)

# addOnStoreCreateListener(onCreated) → {function}

It can be used to execute a function once a redux store has been created.

Parameters:
Name Type Description
onCreated function

The function which will be called when redux store is created

Unsubscribe function

function
Example
externalCodeSetup.reduxApi.addOnStoreCreateListener((props) => {
 //Redux store has been created!
 //Do something here..
})

# addPersistorConfigChanger(changer) → {Object}

You can use this to change config for redux persistor. For example, adding a new reducer to the whitelist.

Parameters:
Name Type Description
changer function

Function accepts the default persistor config; Or the changed persistor if externalCodeSetup.reduxApi.overidePersistorConfig is used.

PersistorConfig

Object
Example

Add new reducer to whitelist

externalCodeSetup.reduxApi.addPersistorConfigChanger(props => {
 const changedPersistor = {
  ...props,
  whitelist: [
    ...props.whitelist,
    "customReducer"
  ]
 };
 return changedPersistor
})

# addReducer(name, reducer)

Adds a new reducer to the root reducer. Note: If "name" used to add a reducer already exists, the new reducer will replace the old reducer.

Parameters:
Name Type Description
name String

Name of reducer

reducer Reducer

Reducer function

Example

Create a new reducer

 const initialState = {
   loading: false,
   loaded: false,
   errorMessage: null,
   foodReceived: ""
 }
 const delivery = (state = initialState, action) => {
   switch (action.type) {
     case "FOOD_DELIVERY_REQUEST": {
       return {
         ...state,
         loading: true,
         loaded: false,
         errorMessage: null
       };
     }
     case "FOOD_DELIVERY_SUCCESS": {
       return {
         ...state,
         foodReceived: action.foodToDeliver,
         loading: false,
         loaded: true,
         errorMessage: null
       };
     }
     case "FOOD_DELIVERY_FAIL": {
       return {
         ...state,
         loading: false,
         loaded: false,
         errorMessage: action.errorMessage
       };
     }
     default:
       return state;
   }
 };

 externalCodeSetup.reduxApi.addReducer("food", delivery)

# overidePersistorConfig(config)

You can use this to set custom persistor configuration. It will replace the default configuration persistor from the app. Persist is used to store Redux state on disk so that it can be available after reopening the app without fetching data again. You can refer to this link for more information: https://github.com/rt2zz/redux-persist

Parameters:
Name Type Description
config Object

App persistor config

Example

Default configuration from app

...

import AsyncStorage from "@react-native-community/async-storage";

export const applyCustomCode = externalCodeSetup => {
 const persistConfig = {
  storage: AsyncStorage, // where to store
  whitelist: [ // names of reducers to store
    "auth",
    "activities",
    "blog",
    "messages",
    "forums",
    "menuSettings",
    "user",
    "sites",
    "settings",
    "blockData",
    "blockLoadableDataCache",
    "activitiesCache",
    "usersCache",
    "topicsCache",
    "forumsCache",
    "coursesCache",
    "groupsCache",
    "messagesCache",
    "blogCache",
    "socialGroups",
    "members",
    "localAuthentication",
    "urls",
    "inAppPurchases",
    "singleCourse",
    "emailInvites"
  ],
  transforms: [reducerTransform, transformSerialiseImmutable],
  debounce: 300,
  key: "v5"
 };

 externalCodeSetup.reduxApi.overidePersistorConfig(persistConfig);
}

# setIsDataPersistingEnabled()

If set to false, redux data won't be saved to disk.

Example
externalCodeSetup.reduxApi.setIsDataPersistingEnabled(false)

# wrapEpic(name, epicWrapper)

You can use it to wrap an epic with another epic. It can be used to filter action observables of existing epics.

Parameters:
Name Type Description
name AppEpic
epicWrapper EpicWrapper
Example

Observes for type "CUSTOM_LOAD_COURSE_REQUEST" in "loadCoursesOnRequest" epic

const filterActionsWrapper = (originalEpic: Epic): Epic =>
 (
   action,
   storeApi,
   dependencies
 ) => {
   const filteredInputActions =
     action
       .filter(
         a =>
           a.type === "CUSTOM_LOAD_COURSE_REQUEST"
       )
   return originalEpic(filteredInputActions, storeApi, dependencies);

};
externalCodeSetup.reduxApi.wrapEpic("loadCoursesOnRequest", filterActionsWrapper);

# wrapReducer(name, reducerWrapper)

Wraps original reducer with another reducer. It can be useful if you want to change original reducer behavior without replacing it.

Parameters:
Name Type Description
name AppReducers

Name of reducer to wrap

reducerWrapper ReducerWrapper
Example

Add a button which removes an item from the courses list

//In custom_code/components/CourseItem.js

import React from 'react';
import { View, Text, TouchableOpacity, Button } from "react-native";
import { WidgetItemCourseUserConnected } from "@src/components/Widgets/WidgetItemCourseUser";
import { useDispatch } from "react-redux"

const NewWidgetItemCourseComponent = (props) => {

   const { viewModel, global, colors } = props;

   const dispatch = useDispatch();

   return <View>
       <View style={{ margin: 20, flexDirection: "row" }}>

           <TouchableOpacity onPress={viewModel.onClick}>
               <Text>
                   {viewModel.title}
               </Text>

               <WidgetItemCourseUserConnected
                   lightText={true}
                   global={global}
                   userId={viewModel.authorId}
                   colors={colors}
               />

           </TouchableOpacity>
       </View>

       <View>
           //Use dispatch() from react-redux to dispatch an action
           <Button title="Remove from list"
           onPress={() => dispatch({ type: "COURSE_REMOVE_ITEM", courseToRemove: viewModel.id })} />
       </View>

   </View>

}

export default NewWidgetItemCourseComponent;

//In custom_code/index.js...

...

import CourseItem from "./components/CourseItem"
export const applyCustomCode = externalCodeSetup => {

 externalCodeSetup.coursesHooksApi.setWidgetItemCourseComponent(CourseItem);

  const reducerName = "courses"; // "courses" reducer can access data displayed in courses list

  //Initialize the custom reducer
  const customReducer = reducer => (state = reducer(undefined, {}), action) => {

   switch (action.type) {

     case "COURSE_REMOVE_ITEM":

       //Use variables below to get index of id which will be removed from the list
       const removeWithIndex = state.all.ids.indexOf(action.courseToRemove);
       const newIds = state.all.ids.splice(removeWithIndex, 1)

       //Assign new state with "newIds"
       const newState = {
         ...state,
         all: {
           ...state.all,
           ids: newIds
         }
       }

       return reducer(newState, action);

     default:
       return reducer(state, action);
   }

 }

 externalCodeSetup.reduxApi.wrapReducer(
   reducerName,
   customReducer
 );
}