Cycle.js tutorial: Registration Form - Part 2
This tutorial is the second part of our previous tutorial where we built a simple registration form using Cycle.js. In this part, we will introduce some basic concepts for building Cycle.js like seeds, intents and state.
Table of Contents
- 1. Cycle.js tutorial: Registration Form - Part 2
- 1.1. About Cycle.js
- 1.2. Create the application boilerplate
- 1.3. Defining the state update function
- 1.4. Defining seeds for the application state
- 1.5. Defining the application intents
- 1.6. Defining our application state
- 1.7. Constructing the virtual DOM tree
- 1.8. Returning the output sinks
- 1.9. Links
1 Cycle.js tutorial: Registration Form - Part 2
This tutorial is the second part of our previous tutorial where we built a simple registration form using Cycle.js. In this part, we will introduce some basic concepts for building Cycle.js like seeds, intents and state. Online demo is available here. Source code for this tutorial is available in Github.
1.1 About Cycle.js
Cycle.js is a functional and reactive JavaScript framework for predictable code created by Andre Staltz from Futurice.
1.2 Create the application boilerplate
The quickest way to create a new project with Cycle.js is by using create-cycle-app.
npm install --global create-cycle-app
create-cycle-app my-awesome-app
The application folder structure will look like this:
Inside the src/ directory, we will be having the source code:
- app.js => will have your app source code, this is where we will be putting our logic
- main.js => will have main() function which will be executed by Cycle.run
- app.test.js => will have tests for your app
1.3 Defining the state update function
In this section we will define a simple function for updating our application state based on the user actions automatically.
const scanFn = curry((state, updateFn) => {
return updateFn(state);
});
We are using the curry function from ramda, which is a practical functional programming library for Javascript programmers. Unlike the likes of underscore.js and lodash, ramda really shines as a truly functional programming library for Javascript. If you want to learn more about ramda, go to their official documentation.
1.4 Defining seeds for the application state
For any application, we need to start from somewhere to initialize the application state to a bare minimum, so that the user will have something to interact with. Seeds are the things which rightfully serve the exact place for the initial application state.
const seeds = {
username: '',
email: ''
};
1.5 Defining the application intents
Here we will be defining the actual user actions which in turn modify the application state as a collective object so that it will be easier for the developer to maintain the actions in a single place.
Model-View-Intent (MVI) is reactive, functional, and follows the core idea in MVC. It is reactive because Intent observes the User, Model observes the Intent, View observes the Model, and the User observes the View.
For every DOM element with which the user will be interacting with we need to define a set of actions or inputs for which the application state will be reacting to. In our case, we will be interacting mainly with our input boxes, and for each input field we wil be doing the following:
- From the DOM Source get the input dom element
- Capture only the input events for the element
- Map each event to the value of the input element i.e., the target
To know more about the MVI architecture, go the Cycle.js documentation
const intents = {
changeUsername: sources.DOM.select("#username")
.events('input')
.map((event) => event.target.value),
changeEmail: sources.DOM.select("#email")
.events('input')
.map((event) => event.target.value)
};
1.6 Defining our application state
Its time to define our application state based on the intents which we have created previously. Our state is a single stream got by merging the two intents changeUsername and changeEmail and mapping their corresponding values.
const state$ = xs.merge(
// Track fields
intents.changeUsername.map((v) => assoc('username',v)),
intents.changeEmail.map((v) => assoc('email',v))
)
.fold(scanFn,seeds)
.remember();
1.7 Constructing the virtual DOM tree
Here we are constructing the Virtual DOM using the application state. We are using React like JSX syntax here, since it feels natural to write HTML markup in HTML rather than using some little confusingly hyperscript syntax. For our Virtual DOM, we use snabbdom-jsx which will provide you to write some HTML markup inside Javascript.
const vtree$ = state$.map((state) => {
return (
Registration
============
Username:
Email:
* * *
State SPY
---------
{ JSON.stringify(state,null,2) }
);
});
Also, we are capturing the application state using the values stored in username and email and rendering them in the DOM using pre tags in JSON format.
1.8 Returning the output sinks
Finally we are returning the virtual DOM tree as output sinks back again to the main function.
const sinks = {
DOM: vtree$
};
return sinks;