# 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~~