Data Containers
Introduction
This layer carries out the state management, API call, and API response mapping processes for the application. For state management, Redux is used with thunk middleware.
The data containers exported in the dataContainers/index.js
path are sent to the createApp function as a parameter with the dataContainers key. The Redux store is created with the dataContainers sent with the createApp function parameter.
Concept Definitions
Actions
Redux actions are defined. A new class is created in the base action class and exported. Click for the base actions list.
Example
import { BasketActions } from '_dataContainers/baseContainers/basket';
class Action extends BasketActions {}
export default Action;
Constants
Redux action types are defined. A new instance is created in the base action class and exported. Click for the base constants list.
Example
import { BasketConstants } from '_dataContainers/baseContainers/basket';
class Constants extends BasketConstants {}
export default new Constants();
Model - Mappers
API responses are mapped based on integration maps configs. Base model is imported, and a new class is created with base maps and exported.
Click for the base models list.
Example
import { BasketModel } from '_dataContainers/baseContainers/basket';
const { BasketMappers } = BasketModel;
class Basket extends BasketMappers {}
export default Basket;
Model Binders
The action types in the redux are mapped with the functions in model-mappers, and the action’s payload is sent to the mapper function as a parameter. A new instance is created in the base model binders class and exported.
Click for the base model binders list.
Example
import { BasketModelBinders } from '_dataContainers/baseContainers/basket';
class ModelBinders extends BasketModelBinders {}
export default new ModelBinders();
Reducer
Redux reducers are defined.
A new instance is created in the base reducer class. The new instance is exported from the framework with the convertReducer utils.
Click for the base reducers list.
Example
import { BasketReducer } from '_dataContainers/baseContainers/basket';
import { convertReducer } from '_utils/dataContainer';
class Reducer extends BasketReducer {}
export default convertReducer(new Reducer());
Create New Container
Below is an example where data containers are created for the search feature.
The following folder structure is created in the dataContainers path for the custom container.
constants/index.js
Redux action types are exported.
actions/index.js
Redux actions are defined for the API call.
modelBinders/index.js
Redux action types are mapped with the model mapper functions. If the defined action type is dispatched, the action payload is sent to the function as a parameter.
import constants from '../constants';
const { FETCH_SEARCH_FULFILLED_CUSTOM } = constants;
class ModelBinders {
defaultBinders = {
[FETCH_SEARCH_FULFILLED_CUSTOM]: 'searchMapper',
};
}
export default new ModelBinders();
model/index.js
The class instance or object including the functions defined in modal binders must be exported. The data retrieved from the action payload is sent to the function as a parameter. The API response is standardized with integration maps configs, and modeled data is sent to reducer with the data key.
reducer/index.js
A class is created for the redux reducer, and this class must have a function named handleAction.
Modeled action is sent to the handleAction function as a parameter. The created class instance is exported with the convertReducer function.
index.js
The data container must be initialized and exported from the BaseContainer class with the keys seen below.
import BaseContainer from '@core/dataContainers/baseContainer';
import actions from './actions';
import model from './model';
import reducer from './reducer';
import constants from './constants';
import modelBinders from './modelBinders';
const basketContainer = new BaseContainer({
reducer,
model,
actions,
constants,
modelBinders,
})
export default basketContainer;
Extend Framework Container
Base containers may not always fulfill requirements, and they can be expanded based on needs.
Exemplary Scenario:
The order cancel action within the application does not fulfill the needs of the customer. Order ID should be sent at the end of the URL instead of within the request body.
It’s possible to make the necessary changes to meet the customer’s needs by overriding the postCancelForm function in the OrderActions class.
// dataContainers/orders/actions/index.js
import { OrdersActions } from '_dataContainers/baseContainers/orders';
import { platformConfigs } from '_dependencies';
import { request } from '_core/request';
import { textGenerator } from '_utils/string';
const {
integrationMaps: { urls: $urls },
} = platformConfigs;
class Action extends OrdersActions {
postCancelForm = (orderItems, orderNumber, orderId) => (dispatch) => {
dispatch(this.pending());
request({
method: 'post',
url: textGenerator($urls.CANCELLATION_REQUEST, { orderNumber }),
data: {
cancel_order_items: orderItems.map((item) => ({
order_item: item.id,
reason: item.reason.id,
cancellation_type: item.reason.type,
description: item.reason.label || '',
})),
},
headers: {
'x-requested-with': 'XMLHttpRequest',
'referer': `${$urls.BASE_URL}${$urls.ORDERS}`,
},
})
.then(() => {
dispatch(this.fetchOrderDetail(orderId));
dispatch(this.orderItemCancellationRequest(orderItems));
})
.catch((error) => {
dispatch(this.cancelRejected({ apiError: error }));
});
};
}
export default Action;
Here’s how to update the constants class created to add a new constant:
/ dataContainers/search/constants/index.js
import { SearchConstants } from '_dataContainers/baseContainers/search';
class Constants extends SearchConstants {
constructor() {
super();
this.NEW_CONSTANT = 'NEW_CONSTANT'
}
}
export default new Constants();