Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/theKashey/react-gearbox

⚙️📦 Gearbox - Renderless state provisioning and composition
https://github.com/theKashey/react-gearbox

compose fractal react render-props

Last synced: 3 months ago
JSON representation

⚙️📦 Gearbox - Renderless state provisioning and composition

Awesome Lists containing this project

README

        

⚙️📦 GearBox
=======
[![Build Status](https://travis-ci.org/theKashey/react-gearbox.svg?branch=master)](https://travis-ci.org/theKashey/react-gearbox)
[![coverage-badge](https://img.shields.io/codecov/c/github/thekashey/react-gearbox.svg?style=flat-square)](https://codecov.io/github/thekashey/react-gearbox)
[![NPM version](https://img.shields.io/npm/v/react-gearbox.svg)](https://www.npmjs.com/package/react-gearbox)

Composes renderless containers and manages them in afterlive.
Heavily inspired by [react-adopt](https://github.com/pedronauck/react-adopt)(context compose),
[react-powerplug](https://github.com/renatorib/react-powerplug)(renderless containers)
and [redux-restate](https://github.com/theKashey/restate)(fractal state).

The purpose of this library is
- (torque) combine "container"(plugs, context, states), to form more complex structure (gearbox).
- (transmission) provide a way to access them down the tree (train).
- (gear train) provide a way to alter their work (transmission).

That's why - gearbox

# API

```js
import {gearbox} from 'react-gearbox';

// consume any "headless" component
const State = gearbox({
name: ,
nick: ,
team: ,
enemies: Context.Consumer
});

// create state and access via renderprops

{({nick}) => cos I am {nick.value}}

// create state, and access it later using train

cos I am {({nick}) => nick.value}

// unwrap powerplug using transmission

({nick: nick.value})}>
cos I am {({nick}) => nick}

```

* `gearbox(gears, options?): Gearbox` - creates a Gearbox component. Where
* `gears` is a shape of different _render-prop-ish_ components, for example:
- ReactElements, or
- FunctionalStatelessComponents, or
- Context.Consumers.

* `options` is an optional field.
- options.transmission(input, props) => output - build in transmission, to be applied on gears.
- options.defaultProps - set default props for a future component (note: defaultProps are not reflected on types, PR welcomed)

Produces a `Gearbox` - renderless container, which will provide _torque_ from all gears as a render prop.

* `transmission(gearboxIn, clutch): Gearbox` - created a devired Gearbox, with "clutch" function applied to all stored data.
leftMenuController: ,
topMenuController: ,
toggler: gear(Toggle, { initial: {} }), // ~
`Gearbox` is a compound component, and includes 2 sub components
* Gearbox.train - _React Context_ based long-range torque provider, which will provide access to the parent Gearbox from the nested components.
* Gearbox.transmission - establish a local (declarative) transmission. Might not be type safe.

`Gearbox` has only one prop - `render`, if not set - children is a ReactNode. If set - renderProp(function as a children)

`gear(component, props)` - a small helper to create elements, without mocking `children` prop

## Rules
- `Gearboxes` are used to combine gears together, and put them into context.
- `trains` - to access distant gearbox.
- `transmission` - to adapt data structure for the local needs.

## Adaptation
Gearbox could merge output from different component using the keys as names.
But sometimes you need a bit another structure, for example - just rename fields.
```js
import {gearbox, gear} from 'react-gearbox';
import {Toggle} from 'react-powerplug';

const Gearbox = gearbox({

}, {
transmission: ({leftMenuController, topMenuController}) => ({
isLeftMenuOpen: leftMenuController.value,
isTopMenuOpen: topMenuController.value,

toggleLeftMenuOpen: leftMenuController.toggle,
toggleTopMenuOpen: topMenuController.toggle
})
});
```

In the same way - you can create a new Components

```js
const Switch = gearbox({
toggle: props => ,
}, {
transmission: ({toggle}) => ({
enabled: toggle.on,
switch: toggle.toggle
}),
defaultProps: {
render: true, // render props as default
local: false, // no context support,
pure: true, // behaves as a pure component (or readux-connect)
}
});

// new component adopted!

{({enabled, switch}) => ... }

```

The same technique could be used to achieve the same results as recompose's `withHandlers`
> While gearbox itself is `withState`.

# Observed bits
Gearbox utilizes React.Context `observerBits` feature, not calling `Trains` if data they consume not changed.
With `pure` option enabled this gives you fine control over update propagation flow.

You may opt-out by using `Gearbox.directTrain`.
```js
{({value1}) => will only update, when "value1" got updated}
{({value1}) => will update on any GearBox update}
```

# Debugging

Gearbox also provides a _fancy_ debugging. Just double check React Dev Tools.

In addition:
- setDebug(boolean | function) - enableds low-level debug.


# Examples

1. Create a gearbox
```js
import {gearbox} from 'react-gearbox';
import {Value, Toggle} from 'react-powerplug';


const Gearbox = gearbox({
// pass a pre-created Element, as you could do with react-adopt
storedValue: ,

// pass component, to initialize Element from props (applied only on mount)
toggle: props => ,

// pass React.Context
data: reactContext.Consumer as any, // !! "pure" consumers are not "type safe"

// or pass it as React.Element
context: ,

// you may access all the gearings from above
smartComponent: (props, {data, context} /* all the props from above*/) =>

// Unstated container? Anything "render-prop" capable will work.
stated: ,
});
```

2. Use Gearbox with or without renderprops

By default Gearbox __expects ReactNode as a children__, and data to be accessed via `train`, but you may specify `render` prop, to change this behavior to function-as-children.

> `render` stands for renderProps, `on` is required by `toggle`, so required by Gearbox
```js
const App = () => (

{({storedValue, toggle, data, stated}) =>

...
}

)

const App = () => (

...


)
```
3. Use Gearbox.train to get the data
```js
// once Gearbox is assembled - you can access it elsewhere using gear trains

const Children = () => (

{({storedValue, toggle, data, stated}) =>
...
}

)
```

4. Use Gearbox.transmission to adopt the data
```js
// component based Transmission are NOT type safe
const Transmission = () => (
({on:toggle.on, toggle: data.toggle})}
>
{/* you may use */ }
{({on, toggle}) =>

...
}


)

const Transmission = () => (
({on:toggle.on, toggle: data.toggle})}
render /* use as render prop */
>
{({on, toggle}) =>
...
}

)
```

4. Use transmission to achieve type safe transmission.
```js
const TransmittedGear = transmission(Gearbox, ({toggle, data}) => ({on:toggle.on, toggle: data.toggle}));
```

# Licence
MIT