Reusable Event State -- An Anti-pattern

Angular: The Full Gamut

Charlie Greenman
September 15, 2020
3 min read

The Idea - Reusable Event State

Once upon a time, I was creating a re-usable component, and I was considering ways of having the entire application know of the state of the component at any given time. After thinking about it quite a bit, I decided to go with a re-usable state approach. How would I, however, create re-usable state, for a reusable component? I was using ngrx/store for state management and as the pattern progressed, I also ended up using ngrx/entity for easily managing my collections.

Nested Data and storeSelectName Key

The secret for creating a re-usable state lies in nested data, and passing a storeSelectName key through the action’s payload. This is so that dynamic stores’(so to speak), can be created. When using functional data composition, really, this isn’t all that difficult to manage. Following this approach, one’s (reducer)code will end up looking something like this:

// Business logic obfuscated for article purposes. 
export const adapter: EntityAdapter<any> = createEntityAdapter<any>({
  selectId: (tableStore: any) => tableStore.id,
  sortComparer: false,
});

export function tableReducer(
  state = tableInitialState,
  action: tableAction
) {
  const storeSelectName = action.storeSelectName;
  function nestedStore(data) {
    return {
      ...state,
      [storeSelectName]: {
        ...data,
      },
    };
  }
  function nestedStoreSelected(data) {
    return nestedStore({
      ...state[storeSelectName],
      selected: { ...data },
    });
  }

  switch (action.type) {
    case DataTableTypes.LoadData: {
      return nestedStore(
        adapter.addAll(action.payload, state[storeSelectName])
      );
    }
    case DataTableTypes.SelectData: {
      return nestedStoreSelected(
        adapter.addOne(action.payload, state[storeSelectName].selected)
      );
    }
    case DataTableTypes.DeSelectData: {
      return nestedStoreSelected(
        adapter.removeOne(action.payload.id, state[storeSelectName].selected)
      );
    }
    case DataTableTypes.UpdateRow: {
      return nestedStore(
        adapter.upsertOne(action.payload, state[storeSelectName])
      );
    }
    default: {
      return state;
    }
  }
}

Going Back to the Drawing Board — Smart and Dumb Components

After going through the codebase again I determined that re-usable state is an anti-pattern. The reason is like this. As soon as a software engineer recognizes that a component is going to be re-usable, that means that there is going to be a smart, and dumb component. As soon as there is a smart and dumb component, they should handle all event handling and not the state. It makes it easier for things to be managed that way.

An Example of Why This Is

For instance, in the above code, it deals with logic for select and de-select directly in state. Instead, there should be an event emitter within the UI that the smart component can pick up on for selecting(without using state). This pattern should be followed for any UI related event(clicking, dropping, sorting etc.). Without going into all use cases, if one follows this approach, one will find that re-usable state is completely superfluous, and is an anti-pattern. In addition, your life will be made easier without using it, especially with regards to type checking and explicit unit testing.

Thank you for reading.

More articles similar to this

footer

Razroo is committed towards contributing to open source. Take the pledge towards open source by tweeting, #itaketherazroopledge to @_Razroo on twitter. One of our associates will get back to you and set you up with an open source project to work on.