A reactive frontend framework for JavaScript
npm install cyclow
cyclow is a reactive frontend framework for JavaScript. It's inspired by another frameworks like [Cycle.js] and [TSERS]. It uses [graflow] as stream library.
This a simple counter example:
``js
import { Block, run } from 'cyclow'
const Counter = () => Block({
on: {
'in.init': () => state => 0,
'dom.click': () => state => state + 1
},
view: state => ({
tag: 'button',
on: {click: 'click'},
content: Count: ${state}
})
})
run(Counter)
`
Try it online!
git clone https://github.com/pmros/cyclow
cd cyclow
npm install
npm run samples
`Samples include a TodoMVC sample.
You can find even more samples at [JS Comp] and compare them with another implementations using frameworks like React, Angular or Cycle.
Why cyclow?
There are many JavaScript frameworks so... why another one? Well I really like [Cycle.js]. It's a nice reactive framework. [TSERS] is like [Cycle.js] and it adds a simple state manager and another features. But both are too pure (in the functional programmming sense) for me.With cyclow instead of thinking in a big global model and pure functions, you have to think in components with inputs, outputs and their own state (something like an electronic circuit). I think cyclow is more intuitive and easier while it's still reactive and quite declarative. You can compare cyclow and [Cycle.js] samples at [JS Comp].
cyclow goal is to help you to create applications that are:
- Declarative
- Easy to code
- Easy to read
- Scalable
How it works
A cyclow app consits of a block. A block is composed of other blocks. A block is [graflow] component, it receives messages and send messages async.Every block contains this default blocks:
-
in
- events
- state
- view
- dom
- outIn adition, you can add your own blocks or custom blocks with
blocks Block option.Every block inside a block is connected through a
bus block, sending and receiving messages. Bus connect blocks forming a cycle.Messages has three parts:
- Block
- Signal
- Value
You can handle messages with
on Block option.Finally, you can transform state into a Virtual DOM Element with
view Block option. Virtual DOM Element will be converted into a real DOM by the renderer.How To
$3
At the beginning, every block receives an init signal from in block. So you can handle this message to set a initial state.From the counter example:
`js
on: {
'in.init': () => state => 0
}
`In this case, the handler takes no params
() and returns a state transformation state => 0. It's a function that takes the current state and returns the next state.
$3
First, you have to catch the DOM event in view Block option. From counter exaple:
`js
view: state => ({
tag: 'button',
on: {click: 'click'},
content: Count: ${state}
})
`Then, you can handle DOM event as a normal block message (from
dom block):
`js
on: {
'dom.click': () => state => state + 1
}
`If you need DOM event information, see Inputbox sample:
`js
on: {
...
'dom.text': newText => text => newText,
},
view: text => ({content: [
{tag: 'input',
attrs: {id: 'myInput', value: text},
on: {keyup: (e, next) => next({text: e.target.value})}
}
...
]})
`$3
See Composition sample:
`js
blocks: {field: Field()},
on: {
'in.init': () => [{'field.init': 'Enter name'}, state => ({current: 'Steve'})],
'field.submission': submission => state => ({current: submission})
}
`$3
See Inputbox sample:
`js
on: {
'dom.focus': () => ({'dom.action': node => node.firstElementChild.focus()})
}
`$3
See TodoMVC sample:
`js
on: {
'in.init': () => state => JSON.parse(localStorage.getItem('todomvc')) || initial(),
state: state => { localStorage.setItem('todomvc', JSON.stringify(state)) }
}
`$3
You can log every message through bus block:
`js
on: {
bus: msg => {
console.log('Message', msg)
return msg
}
}
`
Virtual DOM Element
cyclow represents DOM elements as Virtual DOM Elements, that is a simple Javascript object with the following (optional) properties:
- tag: HTML tag (default is div)
- attrs: Attributes (like id, class or style).
- on: Events handlers (like click). It can be just an event message or a function that receive the DOM event and a function to send an event message.
- content: Content can be just text, a Virtual DOM Element or an array of Virtual DOM Elements.This is a virtual DOM element example:
`js
{
tag: 'input',
attrs: { id: 'myInput' },
on: { keyup: (e, next) => next({text: e.target.value}) }
}
` Renderer
A renderer is just a component factory. It creates a component that takes a Virtual DOM Element as a input and it converts into a Real DOM Element and it updates the HTML document. cyclow uses snabbdom as default renderer.A renderer is a function that it takes
target, that the DOM element id where you want to insert into the Virtual DOM Element. If you don't specify target, cyclow will append the app at the end of body.API
$3
Arguments:
- MainComponent: A component factory.
- options:
- target (document.body by default)
- renderer (SnabbdomRenderer by default)
- init ({} by default)$3
Arguments:
- options:
- blocks
- on
- view`Returns: A [graflow] component
[graflow]: https://github.com/pmros/graflow
[Cycle.js]: https://cycle.js.org
[TSERS]: https://github.com/tsers-js/core
[JS Comp]: http://jscomp.netlify.com/