garfio
Version:
extending Hookleton Pattern namespaces and more
372 lines (258 loc) • 11.3 kB
Markdown
<h1 align="center">
<p align="center" style="font-size: 1em">GARFIO</p>
</h1>
[](https://www.npmjs.com/package/garfio)
[](https://coveralls.io/github/bySabi/garfio?branch=master)
[](https://opensource.org/licenses/MIT)
[](https://www.paypal.me/bySabi/10)
Garfio is a module composed of several submodules that try to facilitate the use of the _hookletons_. A [hookleton](https://github.com/bySabi/hookleton) is a globalized React Hook, this means that once declared its namespace it is linked to a single state.
The `Hookleton` library includes only the minimum core code needed to maintain state synchronization between "the users" of the hookleton, but was designed to be fully extensible. The submodules of `Garfio` are extensions of this code to be able to use the same hookleton _namespace_ with different states.
_Garfio_ solves the problem of namespaces using several user-selectable approaches. In the future, more approaches could be added if relevant.
> It is recommend take a look first to [Hookleton](https://github.com/bySabi/hookleton) package Doc
## Benefits
- Small size
- Zero dependencies (only Hookleton that depends on React Hook)
- 👉 without using React Context
- 👉 not complex user *memoizations* needed. Out of the box performance
- Works in any environment that supports React Hook: _React Native_, _React Server-Side Rendering ([next.js](https://github.com/zeit/next.js/))_, _[Proto Native](https://github.com/kusti8/proton-native)_, ...
## Installation
```bash
# NPM
npm i garfio
# Yarn
yarn add garfio
```
## External resources
- Medium: [Introduccing Hookleton: How to avoid the “Coupling of Concerns(CoC)” in React Apps](https://medium.com/introduccing-hookleton-a-simple-solution-to-coc/introduccing-hookleton-how-to-avoid-the-coupling-of-concerns-coc-in-react-apps-383322e5bc3)
## included subModules
- [Container](https://github.com/bySabi/garfio#container-module)
- [Store](https://github.com/bySabi/garfio#store-module)
- [Simple](https://github.com/bySabi/garfio#simple-module)
## Hookleton API
Garfio re-export the Hookleton API. You do not need to install hookleton module explicitly if you want to use it. Just:
```javascript
// createHook from Hookleton module
import { createHook } from 'garfio';
```
# `Container` module
###### Hookleton []() + Container []()
_Container_ create different state scopes within the enclosing tags and __without__ using React *Context*
## API
`createHook(useHook, ...initial?): useHookleton`
### Parameters
- `useHook` is the user provide Hook
- `initial` any number of params that _useHook_ will accept
### Returns
- `useHookleton` returned Hookleton. Called by _non-host_ components
- `useHookleton.Container` Container component for enclosing tags scopes
- `useHookleton.get` function that get the current output of the Hookleton.
For standalone use
## usage Example
[page](https://bysabi.github.io/garfio/counterNested/) | [source](./example/pages/counterNested.js)
```javascript
import { createHook } from 'garfio/container';
function counter(initial = 0) {
const [count, setCount] = useState(initial);
const increment = () => setCount(count + 1);
return [count, increment];
}
const useCounter = createHook(counter);
const CounterContainer = useCounter.Container;
function Button() {
const [, increment] = useCounter();
return <button onClick={increment}>+</button>;
}
function Count() {
const [count] = useCounter();
return <p>{count}</p>;
}
export default () => (
<CounterContainer initialArg={1}>
<Count />
<Button />
<CounterContainer initialArg={3}>
<Count />
<Button />
</CounterContainer>
</CounterContainer>
);
```
## initial arguments API
_Containers_ can be initialized in two ways:
### `initialArg` _prop_
**initialArg** is an array of arguments which is `spread` to the Hook provided by the user. If the value is not an array then it is converted. To pass an array as a start element, it must be enclosed in an array too.
Below are examples of usage and automatic conversions.
_Single argument example:_
```javascript
import { createHook } from 'garfio/container';
const useValue = createHook(useState);
const Container = useValue.Container;
export default () => {
return (
<Container initialArg={3}>
{
/* output number 3 */
useValue()[0]
}
<Container initialArg="three">
{
/* output string "three" */
useValue()[0]
}
</Container>
<Container initialArg={[[1, 2, 3]]}>
{
/* output array [1,2,3] in JSX */
useValue()[0]
}
</Container>
</Container>
);
};
```
_Multiple arguments example:_
```javascript
import { createHook } from 'garfio/container';
const useReduced = createHook(useReducer);
const Container = useReduced.Container;
const reducer = (s, a) => s;
const initial = 0;
export default () => {
return (
<Container initialArg={[reducer, initial]}>
{
/* output number 0(initial) */
useReduced()[0]
}
</Container>
);
};
```
### initial `object properties` like _props_
When initializing hooks that use an initialization object it is possible to declare `object properties` as _props_ of the Container.
_Example:_
```javascript
import { createHook } from 'garfio/container';
const useReduced = createHook(({ reducer, initial }) => useReducer(reducer, initial));
const Container = useReduced.Container;
export default () => {
return (
<Container reducer={(s, a) => s} initial={0}>
{
/* output number 0(initial) */
useReduced()[0]
}
</Container>
);
};
```
> The _Container_ API and its functionality is inspired by the [constante](https://github.com/diegohaz/constate) package although React Context has not been used for its implementation
# `Store` module
###### Hookleton []() + Simple []() + Store []()
If in _Container_ we use the enclosing tags to define different hookleton scopes, with `Store` the scope is linked to a single object
## API
### Store creation
`createStore(hook, ...initial?): Store`
#### Parameters
- `hook` is the user provide Hook
- `initial` any number of params that **hook** will accept
> - `initial` parameters from Store *creation* step has priority over `initial` parameters from Store *use*.
> - Do not pass any parameters in the Store creation if you want to initialize it within some Component (when you `use` the hookleton).
> - Remember that hookletons are global, so they are only initialized once, in the first component rendered. The other initializations will be ignored.
>
#### Returns
`Store` sigle object (unique), with this interface:
```
{
get(): any;
delete(): boolean;
}
```
- `get()` for standalone store usage
> Standalone refers to obtaining the current state of the hookleton from any part of the application, including outside the React components. This state is not updated automatically
- `delete()` for store delete. Return `true` on sucess
### Store `use`
`useStore(store, ...initial?): any`
#### Parameters
- `store` already exist **store**
- `initial` any number of params that provided **hook** will accept
#### Returns
Return value is any value returned by the **hook**
## usage Example
```javascript
import { createStore, useStore } from 'garfio/store';
const one = createStore(useState, 1);
const two = createStore(useState, 2);
const three = createStore(useReducer);
const reducer = (s, a) => s;
const Values = () => {
const [v1, set] = useStore(one);
useStore(two);
const [v2] = two.get();
const [v3, dispatch] = useStore(three, reducer, 3);
// output: 1,2,3
return (
<span>
{v1},{v2},{v3}
</span>
);
};
export default () => <Values />;
```
> The Store API and its functionality is inspired in packages like:
> [shared-state-hook](https://github.com/magnumjs/shared-state-hook),
> [react-hook-shared-state](https://github.com/philippguertler/react-hook-shared-state),
> [reactn](https://github.com/CharlesStover/reactn),
> [react-shared-hooks](https://github.com/dimapaloskin/react-shared-hooks),
> [react-hookstore](https://github.com/jhonnymichel/react-hookstore)
# `Simple` module
###### Hookleton []() + Simple []()
`Simple` module does not add anything new. It simply allows you to use a hookleton without being explicit about the component that will act as _the host_ of the hookleton. In other words, you do not need to use the `useHookleton.use()` API, just `useHookleton()`
## API
`createHook(useHook, ...initial): useHookleton`
### Parameters
- `useHook` is the user provide Hook
- `initial` any number of params that _useHook_ will accept
> **initial** params are required. Any param used in `useHookleton` are ignored.
### Returns
- `useHookleton` returned Hookleton. Called by _non-host_ components
- `useHookleton.get` function that get the current output of the Hookleton.
For standalone use
## usage Example
[page](https://bysabi.github.io/garfio/counter/) | [source](./example/pages/counter.js)
```javascript
import { createHook } from 'garfio/simple';
// useCounter is a useState but global
const useCounter = createHook(useState, 0);
const Increment = () => {
const [, update] = useCounter();
const increment = () => update(s => s + 1);
return <button onClick={increment}>+</button>;
};
// The host component
const Value = () => {
const [count] = useCounter();
return <span>{count}</span>;
};
export default () => (
<div>
<Value />
<Increment />
</div>
);
```
# more Examples
Examples [page](https://bysabi.github.io/garfio/) include:
- Nested Counters with "Container" [page](https://bysabi.github.io/garfio/counterNested/) | [source](./example/pages/counterNested.js)
- Counters 10x40 with "Store" [page](https://bysabi.github.io/garfio/counters10x40/) | [source](./example/pages/counters10x40.js)
- Counter with "Simple" [page](https://bysabi.github.io/garfio/counter/) | [source](./example/pages/counter.js)
# for a deeper insight ...
Please read _Hookleton_ [Doc](https://github.com/bySabi/hookleton/blob/master/README.md)
## Credits
### author
- Félix A.A. <> [@bySabi](https://github.com/bySabi)
### Contributing
- Documentation improvement
- Feel free to send any PR
## License
[MIT](./LICENSE)