This is a very simple abstract base class that provides a private
setState method similar to React which will update the internal state
and notify all listeners. Unlike React though, this method is synchronous
so there's no danger in reading the current state while updating it.
If your container needs some dependencies in order to work you can pass
them through the constructor.
The method takes a component and a mapping of props to state containers and connects them
together so that whenever the containers update their state the view
will be re-rendered. The returned HOC will accept the same props as
the original component, minus the props that will hold the containers.
You can connect the same container to multiple views in a singleton
pattern by just passing the same reference to multiple connect calls.
If you want to see how the state evolves, or who triggered a specific state
mutation, the lib exports a state container which holds the graph of all
state commits made by all the containers:
Each setState call in a container will create a new commit in the state commit graph. Each commit has a checkout method which you can use to travel back in time. Checking out a commit will reset every container's state to the state they held at that moment in time.
Branches
After a checkout, any new state commits will create a new branch. This way no commits are overridden and you can easily go back and forth between different paths of your state flow.
CommitGraphDebug
Since the commit graph is a state container you can easily connect it to
a view to monitor your app's state in real time. The lib exports a view
that is already connected and renders the commit graph in a git tree
fashion and allows you to inspect the commits and perform checkouts.
Decoupling state from your React views has some advantages. First of all,
you can test the state logic and the view logic separately. Secondly,
state will become easier to share between components because it will
set at a higher level from where it can be passed to multiple components.
Lastly, components can become more reusable by accepting a more generic
type of state. This becomes evident if you have components that are
coupled to APIs - by removing that state and creating a prop interface
for it, you can open up the component to receiving similar responses
from other APIs as well.
Guiding principles
Type safety
This lib is written in TypeScript and it makes sure that when you connect
a view to a state container the view will have a prop interface accepting
that type of container.
Dependency Injection
Pulling away the state from the views and connecting them higher up
in the app can lead to loosely coupled components. Views can become more
reusable since their state and actions can be expressed through props
and callbacks. The state containers are simple classes with a very
minimal interface that can be implemented with or without this lib or
with other libs.
Separating state from views enables testing them separately in isolation.
Taking the first example from above, the tests might look something
like this:
This pattern is perfectly valid, though it couples the view to the
state container so it can't be used without it. This increases "out of
the box readiness" at the expense of loose coupling.
Connecting a component inline
importconnectToStatefrom'react-connect-state';
importcontainerfrom'./container';
importCounterViewfrom'./view';
importReactfrom'react';
// Connect the view once, outside your render method.
This is the same as exporting a connected component although it happens
higher up the call stack - the CounterView component is reusable and
can be connected to any container and the component we're exporting
binds it to a particular container, effectively binding itself to that
container.
Expressing dependencies between containers
You can subscribe to containers via their subscribe method so there's
nothing from stopping a container listening to another container: just
pass their instances in the constructor and subscribe to them there.