The globalizer Hook Pattern
globalize your React Hooks without fear using the Hookleton Pattern
Hookleton convert any React Hook in a global hook. A global hook is a function that always returns the same result to each place where it is called. Let's call this result, the hook runtime interface.
When this Hook is use**d for the first time its host
component will become a Singleton of it, hence the name Hookleton**. Naming is hard!, you know.
That said, it might sound a bit complicated but it is not. Hookleton
was created thinking about the ease of use even for an occasional user with the minimum effort. It is likely that when you try it you will not want to use something else because there simply is nothing easier out there.
Does Hookleton make your life a little more easy? Consider
The Hookleton Pattern is a software design pattern that restricts the calls to a provided React Hook to a single component and uses a pub/sub mechanism to manage communication with the rest of user components of the hook
# NPM
npm i hookleton
# Yarn
yarn add hookleton
The Hookleton package exposes createHook
function that does all.
createHook(useHook, ...initial?): useHookleton
useHook
is the user provide Hookinitial
any number of params that useHook will acceptuseHookleton
returned Hookleton. Called by non-host componentsuseHookleton.use
returned Hookleton. Called by the host componentuseHookleton.get
function that get the current output of the Hookleton.
For standalone useOnly one component, the host
, can call created hookleton use
hook and this component must be at the top of the component hierarchy.
A simple example is worth a thousand words
import { useState } from 'react';
import { createHook } from 'hookleton';
// useCounter is a useState but global
const useCounter = createHook(useState);
const Increment = () => {
const [, update] = useCounter();
const increment = () => update(s => s + 1);
return <button onClick={increment}>+</button>;
};
const Decrement = () => {
const [, update] = useCounter();
const decrement = () => update(s => s - 1);
return <button onClick={decrement}>-</button>;
};
// The host component
const Value = () => {
const [count] = useCounter.use(0);
return <span>{count}</span>;
};
// Value componet must be at the top
export default () => (
<div>
<Value />
<Increment />
<Decrement />
</div>
);
The Value
component is the host of useCounter
hookleton for being the first component of the hierarchy that call useCounter.use
.
Remember that useCounter
is composing a useState
which is where all the logic happens.
The Hookleton library includes only the minimal core code needed to maintain state synchronization between the users of the hookleton but was designed to be fully extensible. Take a look at these projects, it could be useful:
Examples page include:
{x,y}
position to 1200 components in Real Time page | sourceHow would it be with React Context vs Hookletons?
The idea is very simple. The first time that a user component of the Hook is instantiated the hookleton is created and the result of the call to user Hook
will be linked to the host
of the hookleton. The user Hook
is the one you want to globalize.
The result to the calls to the hookleton in the host
component will become The source of truth
. The rest of user components will receive a reference to The source of truth on each re-render.
As we said the host
of the hookleton is the first component instance that call .use
hook. This is important because:
host
or in the creation moment with createHook
. Any initial value from the rest of components will be ignored.This is all that you need about Hookletons before start to use it
The first reason is simplicity, but obviously this explanation is not enough. Let's do some history.
A cloudy day googling I was looking for the simplest possible alternative to Redux. For a toy project I needed to share a couple of values between components in the simplest way possible. I found several packages but I did not like any, mainly for two reasons.
This does not mean that they are badly constructed, just that they were not built on top of React Hook, they are prior to it or do not benefit at all of the "Hook engine"
Hookleton solves it elegantly. Actually Hookleton does not know anything about the useHook
that want to be global. This means that its utility is to share the interface of the hook not only its state.
Hookletons can be used in all kinds of projects, both large and small. Remember that Hookleton does not impose anything, it's just a wrapper to useYourImagination
logic
and data
between related and non-related componentsimmutable
data and complex boilerplatesimmutable
data are a requirement, Hooks can be composed using HookletonscreateHook
and when it is called from the host component.
createHook
creation has priorityundefined
in createHook, the value set in the host
component will be used. There is only one host component.initial
values passed to non-host components will be ignoredsetter
will fail. To ensure that this does not happen, you can create a component that acts as the host of the hookleton or several of them at the top of the component hierarchy, Ex:
const Hookletons = () => {
useExample1.use(0);
useExample2.use('one');
useExample3.use({ three: 3 });
return null
}
const App = () => (
<Hookletons />
<rest of the components ...>
)
.get()
, the return value will be an empty array if it is called before render the component that host the hookletonThe speed of Hookleton depends completely on the implementation of React Hook. If you compare it with any implementation based on Hooks, it should have a similar performance.
If you compare it with other non-Hook based solutions, I do not know that it's the fastest thing out there. But the fair comparison would be against the React Hooks himself.
You can see how the rerender behave in the above provide examples: Mouse, Counters, and Fetch data