Great analogy. Capsules are more similar to Recoil than to a larger system like Redux.

Recoil still uses the context API, which solves a problem that may not need solving. The benefit of the context API in extensible state managers is to concurrently render the same application multiple times, e.g. server-side rendering. If your application is rendered in the browser as a singleton, you do not see benefit from the context API, and this magic abstraction can cause confusion for onboarding developers.

The Recoil library also requires unique property keys, meaning each slice of state is aware of every other slice, even if unrelated. This produces a mental overhead of uniquely naming a global state with much the same burden of “safely” putting it on the window object in a manner that it does not conflict with other global states. If I want to create a component library that shares internal state using Recoil, my use of Recoil will either conflict with the RecoilRoot context of the consuming application (1), or my use of my own RecoilRoot within the component library will prevent the consuming application from dependency injecting components that rely on the application’s Recoil context (2).

// 1. My use of Recoil conflicts with the consuming application.
const myLibraryState = atom({
key: 'accidentally-shared-key',
});
// 2. My use of RecoilRoot prevents dependency injection.
function MyLibraryComponent({ children }) {
return (
<RecoilRoot>
<StatefulHeader />
{children}
<StatefulFooter />
</RecoilRoot>
);
}
function App() {
return (
<RecoilRoot>
<MyLibraryComponent>
<StatefulChild />
<MyLibraryComponent>
</RecoilRoot>
);
}

Example 2 may be a lot to take in, so I’ll explain it here. The App component is the application I write as a developer. I’ve wrapped it in a RecoilRoot so that I my Recoil state can be powered by the context API. In StatefulChild, I am using my Recoil state (powered by my RecoilRoot). The MyLibraryComponent component is a hypothetical component I am importing from a third party. It would be in the form import { MyLibraryComponent } from 'my-lib'; instead of written inline. The author of the library has wrapped it in RecoilRoot so that their Recoil state can be powered by the context API. Their StatefulHeader and StatefulFooter components use Recoil state (powered by their RecoilRoot). When my StatefulChild is injected as a child of MyLibraryComponent, it no longer has access to my application’s RecoilRoot. It is now using the library’s RecoilRoot. The state I want is not contained in that root. It will either be missing or incorrect due to a conflicting key. Imagine debugging this as a junior!

With capsules, the state is located by directly referencing the object in memory — not by using a “key lookup” through the context API. By importing the state directly, you will always have the correct value. Abstracting this state lookup away with the context API is solving a problem that does not necessarily need to be solved, creating more confusion and less confidence about how to implement and maintain the application’s state. If using the context API solves a problem for you, Recoil may be the better choice. It’s about using the correct tool for the job, not the same tool for every job.

This last point brings me to a second issue, which I mention in the original article. Having unrelated state coupled together (by sharing keys) encourages putting all state into the same state manager. An application built atop Recoil is likely to put all state in Recoil. Something I hope to achieve with capsules is the adoption of multiple state managers within a single application, allowing the developer to choose the correct state manager for each slice of state, as opposed to having to choose a single state manager for all slices of state. The latter leads to an overkill state manager for particularly small slices, leading to an increased difficulty in testing, refactoring, and maintaining those smaller slices as needed.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store