When developing with Redux, often times we spend time on a lot of boilerplate code, and some common logic tend to become repeated. We can use Meta pattern to avoid code repetition while still taking advantage of Redux features. It is a simple concept and works like this:
In actions we define the actual type in meta
property with name
and type
fields instead of type
property. So a action looks like so:
{
meta: {
// Use logic/flow of this action
type: STATIC_TYPE_NAME
// Name of the field in the reducer so we can obtain/preserve the state of the action
name: IDENTIFIER
}
// Usually it is a combination of STATIC_TYPE_NAME and IDENTIFIER for DevTools/verbosity purposes only
type: DYNAMIC_TYPE_NAME
}
For example, we have a some logic to call API that requests for exchange rate and user profile. It would be cumbersome to write REQUEST_INIT
, REQUEST_SUCCESS
, REQUEST_FAILURE
everytime we have a distinct API call. Normally, it would look something like this:
For some API call to get exchange rate
// create exchangeRateReducer (INIT, SUCCESS, FAILURE)
// create exchangeRateActions (INIT, SUCCESS, FAILURE)
// create exchangeRateActionsCreators (INIT, SUCCESS, FAILURE)
For some API call to get user profile
// create userReducer (INIT, SUCCESS, FAILURE)
// create userActions (INIT, SUCCESS, FAILURE)
// create userActionsCreators (INIT, SUCCESS, FAILURE)
... tens of other API calls with same structure
Instead, by applying meta pattern we can do this:
Initialize apiRequester reducer/actions/action creators
// create apiRequesterReducer (INIT, SUCCESS, FAILURE)
// create apiRequesterActions (INIT, SUCCESS, FAILURE)
// create apiRequesterActionsCreators (INIT, SUCCESS, FAILURE)
And so then we can reuse the logic by dispatching these actions:
// Dispatch action to get exchange rate
{
meta: {
type: REQUEST_INIT
name: EXCHANGE_RATE
}
type: EXCHANGE_RATE_REQUEST_INIT // Auto generated
}
// Dispatch action to get user profile
{
meta: {
type: REQUEST_INIT
name: USER_PROFILE
}
type: USER_PROFILE_REQUEST_INIT // Auto generated
}
Then if we want to do something after the API calls were successful, using redux-saga
or some listener we can do this:
take(USER_PROFILE_REQUEST_INIT, doSomethingWithProfileSaga)
take(EXCHANGE_RATE_REQUEST_INIT, doSomethingWithExchangeRateSaga)
Benefits are two-folds:
- We can reuse the reducer/action/saga logic
- In DevTools we get to see the action type with name of the request