---
# System prepended metadata

title: State Container Libraries

---

# State Container Libraries

Our team has favored small, lightweight, modularized state containers for managing our React state vs. heavier frameworks like Redux. The current options we utilize are:

1. Class-based React higher order components.
2. [Recompose](https://github.com/acdlite/recompose) `withStateHandlers` composed with `mapProps`.
3. [React State Patterns](https://github.com/procore/react-state-patterns)

# Pros/Cons Of Each

### React Class HOC

#### Pros:
+ No need for external library dependency
+ Lots of control and can access React lifecycle methods.
+ Can use React hooks

#### Cons:
+ Lots of boilerplate code
+ Do not scale in terms of code management

### Recompose

#### Pros:
+ Heavily used by many projects 
+ Lots of utility functions.
+ Highly battle tested and known to be stable
+ Very clean interface and much less boilerplate code
+ Great support docs and API

#### Cons:
+ [No longer maintained](https://github.com/acdlite/recompose#a-note-from-the-author-acdlite-oct-25-2018)
+ Difficult to use with React hooks
+ State handlers cannot call eachother unless using `mapProps`.

### React State Patterns

#### Pros:
+ Lightweight single responsibility library
+ Clean interface and much less boilerplate code. (Has syntax that is extremely similar to Recompose `withStateHandlers`) -> See [here](https://github.com/procore/react-state-patterns#using-statehook-util).
+ Very easy to use with react hooks.
+ Convenience util to organize state/handlers into `store = { state, handlers }` pattern without need for recompose's `mapProps`. See: [stateHook](https://github.com/procore/react-state-patterns/blob/master/API.md#statehook).
+ Easy to make updates to since this is a Procore repo.
+ Very easy to create instances of multiple provider patterns -- not just the decorator pattern. See [here](https://github.com/procore/react-state-patterns/blob/master/API.md#statePatterns)

#### Cons:
+ Not heavily used, has not been battle tested. 
+ May be unknown issues that have not been found?
+ Support docs and API are not as detailed and well-formed as recompose's.
+ Can only use with React versions that support hooks: `^16.8.0` 
+ Not an industry standard and so the technology skills learned using it may not transfer to other companies.
+ Ugly npm package name 😅
+ State handlers cannot call eachother when using [stateHook](https://github.com/procore/react-state-patterns/blob/master/API.md#statehook) -- would need to use the [Directly From Hook](https://github.com/procore/react-state-patterns#directly-from-hook) pattern.


# Examples Useages

The following are examples of a simple Counter app using the three outlined approaches.
Each state container will wrap the component:
```jsx
const Displayer = ({ counter: { state, handlers } }) => (
  <>
    <div>{state.count}</div>
    <button onClick={() => handlers.decrementBy(1)}>Decrement</button>
    <button onClick={() => handlers.incrementBy(1)}>Increment</button>
  </>
);
```

### React Class HOC
```jsx
const Container = (WrappedComponent) =>
  class extends React.Component {
    constructor(props) {
      super(props);
      this.state = { count: props.initialValue || 0 };
    }
    
    incrementBy(value) {
      this.setState({
         count: this.state.count + value,
      });
    }
    
    decrementBy(value) {
      this.setState({
         count: this.state.count - value,
      });
    }

    render() {
      return (
        <WrappedComponent
          counter={{
            handlers={
              incrementBy,
              decrementBy
            },  
            state={
              ...this.state,
            }
          }}
          {...this.props}
        />
      );
    }
  };

const App = Container(Displayer);
// <App initialValue={5} />
```

### Recompose
```jsx
import { withStateHandlers, mapProps, compose } from "recompose";


const Container = withStateHandlers(
  (props) => ({ count: props.initialValue || 0 }),
  {
    incrementBy: ({ count }) => (value) => ({ count: count + value }),
    decrementBy: ({ count }) => (value) => ({ count: count - value }),
  }
);

const App = compose(
  Container,
  mapProps(
    ({ incrementBy, decrementBy, ...props }) => ({
      counter: {
        handlers: { incrementBy, decrementBy },
        state: { count: props.count },
      },
      ...props,
    })
  )
)(Displayer);
// <App initialValue={5} />
```

### React State Patterns
```jsx
import statePatterns, { stateHook } from "@procore/react-state-patterns";

const Container = statePatterns(props =>
  stateHook(
    { count: props.initialValue || 0 },
    state => ({
      incrementBy: value => ({ ...state, count: state.count + value }),
      decrementBy: value => ({ ...state, count: state.count - value })
    }),
    "counter"
  )(props)
);

const App = Container.withState(Displayer);
// <App initialValue={5} />
```


### State Containers Needing Updated

The following state containers do not follow the modularized state container pattern (i.e.)
```jsx
fooStore = { state, handlers };
```

_Task: Update these to use `mapProps` to achieve the pattern above._
1. https://github.com/procore/dev-portal/blob/master/app/frontend/react/common/marketplace_form/StateContainer.js
2. ~~https://github.com/procore/dev-portal/blob/master/app/frontend/react/common/ProductionManifestsTable/StateContainer.jsx~~
3. ~~https://github.com/procore/dev-portal/blob/master/app/frontend/react/common/SandboxManifestsTable/StateContainer.jsx~~
4. ~~https://github.com/procore/dev-portal/blob/master/app/frontend/react/developer_apps/show/DeleteAppCard/StateContainer.jsx~~
5. ~~https://github.com/procore/dev-portal/blob/master/app/frontend/react/developer_apps/show/StateContainer.jsx~~
6. ~~https://github.com/procore/dev-portal/blob/master/app/frontend/react/pages/app_analytics/index/Container.jsx~~
7. ~~https://github.com/procore/dev-portal/blob/master/app/frontend/react/top-nav/TopNavReference/StateContainer.jsx~~
8. https://github.com/procore/procore/blob/master/hydra_clients/company_admin_settings/src/views/AppManagement/AppShow/containers/AppShowContainer.jsx
9. https://github.com/procore/procore/blob/master/hydra_clients/company_admin_settings/src/views/AppManagement/AppsIndex/AppsIndexContainer.jsx
10. https://github.com/procore/procore/blob/master/hydra_clients/company_admin_settings/src/views/AppManagement/AppsIndex/components/AutoConnectModal/StateContainer.jsx

The following do not follow the pattern *and* are the old React Class HOC Pattern

_Task: Update these to use the library we decide to use, and ensure they follow the `fooStore = { state, handlers };` pattern._
1. ~~https://github.com/procore/dev-portal/blob/master/app/frontend/react/admin/developer_apps/index/StateContainer.jsx~~
2. ~~https://github.com/procore/dev-portal/blob/master/app/frontend/react/admin/developer_apps/show/StateContainer.jsx~~
~~4. https://github.com/procore/dev-portal/blob/master/app/frontend/react/admin/notifications/form/FormStateContainer.jsx~~
~~5. https://github.com/procore/dev-portal/blob/master/app/frontend/react/admin/notifications/index/StateContainer.jsx~~
~~6. https://github.com/procore/dev-portal/blob/master/app/frontend/react/containers/NotificationReaderStateContainer.jsx~~
~~7. https://github.com/procore/dev-portal/blob/master/app/frontend/react/containers/NotificationsStateContainer.jsx~~
