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 use RESTService.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 the PaymentsSelector.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);