Universal Access Redux modules
Modules in the Universal Access Responsive Web Application communicate between the application and the IBM® Cúram Social Program Management REST APIs and manage data for the API in the Redux store.
This design allows the React components to focus on presentation and reduces the complexity of the code in the presentation layer. Modules manage the communication between the client application and the IBM Cúram Social Program Management REST APIs, including authentication, locale management, asynchronous communication, error handling, Redux store management and more.
Modules typically follow the re-ducks pattern for scaling with Redux
Modules and APIs
Modules consist of collection of artifacts that work together to communicate withIBM Cúram Social Program Management REST APIs and manage the storage and retrieval of the response in the application state. For example, the Payments module is responsible for communicating with the /v1/ua/payments API. For more information about IBM Cúram Social Program Management APIs, see Connecting to a Cúram REST API.
- Models
-
The
models.js
file is your data representation of the response from the API. It must map the JSON response properties to an object that can be referenced within your web application.class UserProfile { constructor(json = null) { if (json) { this.personFirstName = json.personFirstName; this.personMiddleName = json.personMiddleName; this.personSurname = json.personSurname; this.personDOB = json.personDOB this.userName = json.userName; this.userType = json.userType; ... } } } export default UserProfile;
- Utils
-
The
utils.js
file is responsible for the actual communication to the required API. On successful contact with the API, it constructs the model with the response. For simple GET calls, you can useRESTService.get
to handle the API call. For more information, see the RESTService utility.import { RESTService } from "@spm/core"; import UserProfile from "./models"; const fetchUserProfileUtil = callback => { const url = `${process.env.REACT_APP_REST_URL}/user_profile`; RESTService.get(url, (success, response) => { const modelledResponse = new UserProfile(response); callback(success, modelledResponse); }); }; export { fetchUserProfileUtil };
- ActionTypes and Actions
-
Module actions are used to modify the Redux store, like inserting, modifying, or deleting data from the store. For example, the
PaymentsActions
action modifies the payments slice of the store.Some actions include calls to APIs. For example,
PaymentsActions.getData
action calls the v1/ua/payments API and dispatches the result to the payments slice of the store, or sets an error if the API call fails.The
actionTypes.js
file represents the type of action that is being performed. At its core, they are simple string types. For more information, see the Redux Glossary.const FETCH_USER_PROFILE = "UA-CUSTOM/USER_PROFILE/FETCH_USER_PROFILE"; export { FETCH_USER_PROFILE };
The
actions.js
file contains the Redux actions, which are objects that represent an intention to change the application state. They are exported to be accessible to call from a Container component.The following example is a representation of the action that calls the API and attaches the response to the dispatch, but you might further improve by adding fallback behavior.
import { FETCH_USER_PROFILE } from "./actionTypes"; import { fetchUserProfileUtil } from "./utils"; export default class actions { static fetchUserProfile = dispatch => { fetchUserProfileUtil((success, payload) => { if (success) { dispatch({ type: FETCH_USER_PROFILE, payload: payload }); } }); }; }
- Reducer
-
The
reducers.js
file contains the Redux Reducers. Redux Reducers are just functions that take the existing state and current actions and calculate a new state, thus updating the application state.The following example represents a data reducer that updates the state based on the API result. You can implement more complex reducers based on the action to represent API errors or failures or if the API is awaiting a response, like an
isFetchingUserProfile
reducer.Reducers aren’t called from Container components.
import { combineReducers } from "redux"; import { FETCH_USER_PROFILE } from "./actionTypes"; const fetchUserProfileReducer = (state = {}, action) => { if (action.type === FETCH_USER_PROFILE) { return { ...state, payload: action.payload }; } else { return state; } }; const reducers = combineReducers({ fetchUserProfile: fetchUserProfileReducer // room for more reducers! }); export default { reducers };
- Selectors
-
Module selectors are used to query the Redux store. They provide the response to predefined store queries. For example, the
PaymentsSelector.selectData
selector returns the /payments/data slice from the store, and thePaymentsSelector.selectError
selector returns the value of the /payments/error slice of the store.The
selectors.js
file is responsible for retrieving the data from the application state for use in the Container component (and likely passed as props to the Presentational component). It selects information from the state by using the state’s ‘slice’ identifier.export default class selectors { static moduleIdentifier = "UACustomUserProfile"; static fetchUserProfile = state => state[selectors.moduleIdentifier].fetchUserProfile.payload; }
- Index
-
You must export the parts of a module that must be accessible. Instead of creating an
index.js
per module, create one in the module directory that exports the Actions, Model, and Selectors of each custom module. These classes or functions are the only ones that need to be accessed from the container components.// Modules export { default as UserProfileActions } from "./UserProfile/actions"; export { default as UserProfileSelectors } from "./UserProfile/selectors"; export { default as UserProfileModels } from "./UserProfile/models";
Blackbox
Modules are blackbox so are not open to customization or extension. The modules expose actions and selectors to interact with the module. The actions and selectors are APIs that are documented in the <your-project-root>/node_modules/@spm/universal-access/docs/index.html file.
Reusing Universal Access modules in your custom components
You can use the actions and selectors from the universal-access package to connect your custom components to existing IBM Cúram Social Program Management APIs and the Redux store. You can use the react-redux module to connect your components. Examples of this technique can be found in the universal-access-ui features.
For example, the following code is from the PaymentsContainer file in the Payments feature. The code shows how the actions and selectors from the Payments module are connected to the properties of the Payments component.
This pattern is documented extensively in the official Redux documentation.
import { connect } from 'react-redux';
import React, { Component } from 'react';
...
/**
* Retrieves data from the Redux store.
*
* @param state the redux store state
* @memberof PaymentsContainer
*/
const mapStateToProps = state => ({
payments: PaymentsSelectors.selectData(state),
isFetchingPayments: PaymentsSelectors.isProcessing(state),
paymentsError: PaymentsSelectors.selectError(state),
});
/**
* Retrieve data from related rest APIs and updates the Redux store.
*
* @export
* @param {*} dispatch the dispatch function
* @returns {Object} the mappings.
* @memberof PaymentsContainer
*/
export const mapDispatchToProps = dispatch => ({
loadPayments: () => PaymentsActions.getData(dispatch),
resetError: () => PaymentsActions.resetError(dispatch),
});
/**
* PaymentsContainer initiates the rendering the payments list.
* This component holds the user's payment details list.
* @export
* @namespace
* @memberof PaymentsContainer
*/
export default connect(
mapStateToProps,
mapDispatchToProps
)(PaymentsContainer);