# @sunny-g/cycle-reactdom-driver

> (un)official React(DOM) driver for Cycle.js

Latest version **0.1.0-beta.1** (published 2017-04-11) · ISC license · 0 weekly downloads

## Install

```sh
npm install @sunny-g/cycle-reactdom-driver
pnpm add @sunny-g/cycle-reactdom-driver
yarn add @sunny-g/cycle-reactdom-driver
bun add @sunny-g/cycle-reactdom-driver
```

## Health

**Score 30/100 (F)** — status: abandoned.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads; pre 1.0.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 0.1.0-beta.1 |
| Published | 2017-04-11 |
| First published | 2017-04-11 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 4 |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 8 |
| Author | Sunny G |
| Maintainers | sunny-g |
| Keywords | cycle, react, rxjs, mostjs, xstream |

## Links

- npm: https://www.npmjs.com/package/@sunny-g/cycle-reactdom-driver
- Repository: https://github.com/sunny-g/cycle-reactdom-driver
- Issues: https://github.com/sunny-g/cycle-reactdom-driver/issues
- npm.io page: https://npm.io/package/@sunny-g/cycle-reactdom-driver

## Dependencies (4)

- [xstream](https://npm.io/package/xstream.md) ^10.4.0
- [@cycle/run](https://npm.io/package/@cycle/run.md) *
- [@types/react](https://npm.io/package/@types/react.md) ^15.0.21
- [@types/react-dom](https://npm.io/package/@types/react-dom.md) ^0.14.23

## Alternatives

- [mobx-react](https://npm.io/package/mobx-react.md) — 2.8M weekly downloads
- [rc-tree](https://npm.io/package/rc-tree.md) — 2.6M weekly downloads
- [@react-oauth/google](https://npm.io/package/@react-oauth/google.md) — 1.3M weekly downloads
- [@wagmi/connectors](https://npm.io/package/@wagmi/connectors.md) — 877.0K weekly downloads
- [vee-validate](https://npm.io/package/vee-validate.md) — 836.4K weekly downloads

## Recent versions

- 0.1.0-beta.1 (latest) — 2017-04-11

## README

# cycle-reactdom-driver
*(un)official ReactDOM driver for Cycle.js*

<!--## why-->

## installation
```
npm install --save @sunny-g/cycle-reactdom-driver
```

## usage
Basic usage with the Cycle.js [counter](https://github.com/cyclejs/cyclejs/tree/master/examples/counter) example:

```js
import makeReactDOMDriver from '@sunny-g/cycle-reactdom-driver';

function main(sources) {
  // `sources.REACT` ... ???
  const incrementReducer$ = sources.REACT
    .events('increment')
    .map(_ =>
      ({ count }) => ({ count: count + 1 })
    );

  const decrementReducer$ = sources.REACT
    .events('decrement')
    .map(_ =>
      ({ count }) => ({ count: count - 1 })
    );

  const reducer$ = xs.merge(
    incrementReducer$,
    decrementReducer$
  );

  const props$ = reducer$
    .fold((state, reducer) => reducer(state), { count: initialCount });

  // the sink is a stream of a rendered React elements
  const vtree$ = props$
    .map(({ count }) =>
      <div>
        <button
          key="decrement"
          onClick={sources.REACT.handler('decrement')}
        >
          Decrement
        </button>
        <button
          key="increment"
          onClick={sources.REACT.handler('increment')}
        >
          Increment
        </button>
        <p>{`Counter: ${count}`}</p>
      </div>
    );

  return {
    REACT: vtree$,
  };
}

run(main, {
  REACT: makeReactDOMDriver('#main-container'),
});
```

Alternatively, you can choose to ignore the `state` source and Redux store altogether and manage the state yourself (with [onionify](https://github.com/staltz/cycle-onionify) or something similar).

In this next example, we'll map our actions to reducers, and `fold` them into local state manually:

```js
// same imports as before

function main(sources) {
  const incrementReducer$ = sources.REDUX.action
    .select(INCREMENT)
    .map(({ payload }) =>
      ({ count }) =>
        ({ count: count + payload })
    );

  const decrementReducer$ = sources.REDUX.action
    .select(DECREMENT)
    .map(({ payload }) =>
      ({ count }) =>
        ({ count: count - payload })
    );

  // if using `onionify`, `stanga` or something similar,
  // return `reducer$` and map your provided state source stream to `vdom$`
  const reducer$ = incrementReducer$
    .merge(decrementReducer$);

  const props$ = reducer$
    .fold((state, reducer) =>
      reducer(state),
    { count: initialCount });

  // same vdom$ as before
  const vdom$ = props$
    .map(({ count }) =>
      div([
        button('.decrement', 'Decrement'),
        button('.increment', 'Increment'),
        p('Counter: ' + count)
      ])
    );

  // same action$ sink as before
  const action$ = xs.of({
    [DECREMENT]: sources.DOM
      .select('.decrement')
      .events('click')
      .map(_ => decrement(1)),
    [INCREMENT]: sources.DOM
      .select('.increment')
      .events('click')
      .map(_ => increment(1)),
  });

  return {
    DOM: vdom$,
    REDUX: action$,
  };
}

run(main, {
  DOM: makeDOMDriver('#main-container'),
  REDUX: makeReduxDriver(),	// not technically using Redux anymore, so pass no arguments to drivers
});
```

## api

### `makeReduxDriver`

##### parameters:
* `reducer?: Reducer<any>`: The same Redux reducer you'd pass into `createStore`
* `initialState?: any`: The same Redux initial state you'd pass into `createStore`
* `actionsForStore?: string[]`: List of action types that could result in a `store`'s state change
  * every action stream is lazy by default (unless `select`ed within your application)
  * therefore, in order to preserve as much laziness as possible, we use this array to inform the driver to (eagerly) subscribe to and funnel into the `store` only the action streams that contribute to Redux state
* `middlewares?: Middleware[]`: The same Redux middlewares you'd pass into `createStore`

**NOTE:** All parameters are optional in case you only want to use the action source.

Example:

```js
run(main, {
  // ... other drivers
  REDUX: makeReduxDriver(
    reducer,
    { count: initialCount },
    [ INCREMENT, DECREMENT ],
    [],
  ),
});
```

### `redux.action` source

#### `redux.action.select(type?: string): ActionStream | ActionSink`

##### parameters:
* `type?: string`: A stream that emits action objects of the specified by `type`

##### returns:
* `Stream<FSA> | Stream<{ [type: string]: Stream<FSA> }>`: A stream of [FSA-compliant](https://github.com/acdlite/flux-standard-action) action objects
	* **NOTE**: the `meta` property of the action object is an object with the key `'$$CYCLE_ACTION_SCOPE'` - **this key is required** for Cycle.js [isolation](https://cycle.js.org/components.html#components-isolating-multiple-instances)
	* if `type` was omitted, the stream returned is the raw `ActionSink` that was passed into the driver so that you can create your own custom action streams

Example:

```js
const incrementReducer$ = sources.REDUX.action
  .select(INCREMENT)
  .map(({ type, payload, error, meta }) =>
    ({ count }) => ({ count: count + payload })
  );
```

### `redux.state` source

#### `redux.state.select(): StateStream<any>`

##### returns:
* `MemoryStream<any>`: A stream that emits the Redux store's current `state` every time the state has *changed*

Example:

```js
const state$ = sources.REDUX.state
  .select();
```

### `redux` sink: `ActionSink`

##### should return:
- `Stream<{ [type: string]: Stream<FSA> }>`: A stream of objects, where each key is a specific action `type` and each value is the stream that emits action objects of that `type`.

Example:

```js
// INCREMENT, DECREMENT are action type constants
// increment, decrement are action creators

return {
  // ... other sinks...
  REDUX: of({
    [DECREMENT] : sources.DOM
      .select('.decrement')
      .events('click')
      .map(_ => decrement(1)),
    [INCREMENT] : sources.DOM
      .select('.increment')
      .events('click')
      .map(_ => increment(1)),
  }),
};
```

### helpers

#### `createReducer(initialState: any, reducers: { [type: string]: Reducer })`
Combines a set of related reducers into a single reducer

##### parameters:
* `initialState: any`: The initial state of a Redux "state machine"
* `reducers: { [type: string]: Reducer }`: An object whose keys are the action `type`s and the values are  the `reducer`s that respond to those actions and whose signature is `(state: any, action: FSA) => any`

##### returns:
* a combined `reducer` of the same aformentioned signature

#### `makeActionCreator(type: string)`
Creates a shorthand function for creating action objects

##### parameters:
* `type: string`: The action `type` of the action object

##### returns:
* `actionCreator: (payload: any, error: bool = false, meta: object = {}) => FSA`: A function that creates [FSA-compliant](https://github.com/acdlite/flux-standard-action) action objects with the properties `type`, `payload`, `error`, and `meta`

## contributing

#### todo

- ensure typescript typings are correct and comprehensive and exported correctly
- refactor implementation to not require `redux` if not using the state source
- add testing mock action and state sources
- explain contribution process
- add more tests :)
- explain why I wrote this

## license
ISC

---
_Source: https://npm.io/package/@sunny-g/cycle-reactdom-driver · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
