Ironhook
A JavaScript library for reactive programming using React-like Hooks.
Installation
Using yarn
:
yarn add ironhook
Using npm
:
npm install ironhook --save
Motivation
Reactive programming is generally understood to be programming using asynchronous data streams, which form the conceptual basis for libraries like RxJS or xstream. In React, however, reactive programming is not about dealing with streams, but with so-called Hooks.
A component is rendered, whereby side effects are declared based on its current
state (using the
useEffect
Hook),
which in turn can lead to state changes (using the
useState
Hook) and
thus to further renderings.
In contrast to the concept of streams, the concept of Hooks was something I liked from the beginning. I find them very intuitive to read and write. I wanted to use this kind of reactive programming in other areas as well, for example for programming web workers, AWS Lambda handlers, or even JavaScript-controlled robots. Therefore I wrote this library.
Additional note: Meanwhile I also use Hooks written with Ironhook in the context of React applications for observable global singletons (Ironhook subjects) which I want to consume reactively in multiple components. I created ironhook-react for this, it allows easy use of Hooks written with Ironhook in React.
Usage Example
The following is a constructed example that demonstrates the use of this library:
import * as Ironhook from 'ironhook';
function useName() {
const [name, setName] = Ironhook.useState('World');
Ironhook.useEffect(() => {
setTimeout(() => setName('John Doe'), 10);
}, []);
return name;
}
const nameSubject = new Ironhook.Subject(useName);
nameSubject.subscribe({
next: name => console.log(`Hello, ${name}!`),
error: error => console.error('Oops!', error),
complete: () => console.log('Bye.')
});
Output:
Hello, World!
Hello, John Doe!
API Reference
The React Hooks API reference also applies to this library and should be consulted.
Implementation Status
Below you can see the implementation status of the various Hooks provided by React:
Hook | Status |
---|---|
useState |
✅Implemented |
useEffect |
✅Implemented |
useReducer |
✅Implemented |
useCallback |
✅Implemented |
useMemo |
✅Implemented |
useRef |
✅Implemented |
useContext |
❌Not planned |
useImperativeHandle |
❌Not planned |
useLayoutEffect |
❌Not planned |
useDebugValue |
❌Not planned |
Implementation Notes
This library implements the observer pattern:
The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.
Unlike React, Hooks are executed with Ironhook as an observable subject. This
means the results of a single Hook (aka mainHook
) can be consumed by multiple
parties.
The mainHook
is first executed by the first call to
Subject.subscribe(observer)
. The observer
is then notified synchronously
with the first result. All subsequent observers are also notified synchronously
with the latest available result.
All further executions can be scheduled internally with the useState
or
useReducer
Hooks. The mainHook
continues to run, i.e. it maintains its state
and is re-executed on state changes until an error is thrown or the
Subject.complete()
method is called. An error or completion has the same
effect as unmounting a React function component.
Types
class Subject<TValue> {
constructor(mainHook: MainHook<TValue>);
subscribe(observer: Observer<TValue>): Unsubscribe;
complete(): void;
}
type MainHook<TValue> = () => TValue;
interface Observer<TValue> {
next(value: TValue): void;
error(error: Error): void;
complete(): void;
}
type Unsubscribe = () => void;
Note: The Observer
interface is intentionally compatible with the
Observer
from RxJS and the
Listener
from xstream.
Development
Publish A New Release
yarn release patch
yarn release minor
yarn release major
After a new release has been created by pushing the tag, it must be published via the GitHub UI. This triggers the final publication to npm.
Copyright (c) 2020, Clemens Akens. Released under the terms of the MIT License.