Skip to content

Latest commit

 

History

History
234 lines (166 loc) · 9.9 KB

Tutorial-Part-2.md

File metadata and controls

234 lines (166 loc) · 9.9 KB

This is the second part of the Dapp Tutorial. We assume you're familiar with the content of the past chapter.

⟵ 1st Part | 3rd Part ⟶


oo7 Bonds

Now we have our basic dapp harness, we can start introducing more interesting functionality. Without too much ado, let's get started. Head in to src/client/scripts/app.jsx. You'll see our basic file:

import React from 'react';

export class App extends React.Component {
	render() {
		return (
			<div>Hello world!</div>
		);
	}
}

This is a JSX file. Basically, that means it can handle embedded HTML (well, a dialect of XHTML, to be more exact) when describing how React components should be rendered. If this is all new to you, you might want to familiarise yourself with the technology. For now, I'll assume you either know or don't care.

1. Your first Bond

The first thing we'll do is introduce the oo7 library. This introduces into Javascript the notion of reactive values known as "bonds". Reactive values are similar to normal "variables", except that any places in which they are used will automatically update themselves as the value changes. They can be mapped and composed into arbitrarily complex expressions and then placed in UI components to create an effortlessly dynamic UI.

The oo7 package is still very much in development, as such there might be one or two rough edges in this tutorial. Mea culpa. Ensure you are running with the very latest packages. If you still have trouble, come find me on gitter :-)

Our first example will just demonstrate how Bonds can introduce effortless reactivity by dynamically replicating the contents of an editable text field into a <span>.

Import the needed components by placing three lines at the top of app.jsx:

import {Bond} from 'oo7';
import {Rspan} from 'oo7-react';
import {InputBond} from 'parity-reactive-ui';

Next, we need to introduce a new instance of a Bond. It will represent the current contents of a text field. We will initialise it in our class's constructor. Insert these lines directly into the App class declaration.

constructor() {
	super();
	this.bond = new Bond();
}

Your code should now look like this:

import React from 'react';
import {Bond} from 'oo7';
import {Rspan} from 'oo7-react';
import {InputBond} from 'parity-reactive-ui';

export class App extends React.Component {
	constructor() {
		super();
		this.bond = new Bond();
	}
	render() {
		return (
			<div>Hello world!</div>
		);
	}
}

Next we need to create the text entry field and the <span> element (in which the text field's contents will be reflected). We will use a version of Semantic UI's Input element which has been specially modified to propagate the value into a named Bond. This is called InputBond. Similarly, for the <span>, we'll use a special "reactive" version of the span element which is able to accept Bonds as children and values of certain properties; this is known as Rspan. We imported both before.

Change the <div>Hello world</div> line to:

<div>
	<InputBond bond={this.bond} placeholder="Go ahead and type some text"/>
	<Rspan>{this.bond}</Rspan>
</div>

As you see there's not all that much here. We just tell the text field input InputBond to place its value into this.bond and conversely tell Rspan to display that value from this.bond.

Run Webpack and let it watch your files to ensure your dapp is continuously rebuilt. We have defined an alias for you to do this:

npm start

If that doesn't work due to port conflicts try:

webpack --watch

Reloading our dapp page in the Parity Wallet will give a simple form; select the text field and type something. Whatever you type, you will see it reflected in the <Rspan> element next door:

image

2. Transforming Bonds

Bonds don't just have to pass on data; they can also represent transformations on the data. One example of a transform on text would be simple upper-casing. A function to upper-case text would be text => text.toUpperCase(). We can map our Bond with this function, making the <Rspan> display the upper case of whatever we type into the field:

<Rspan>{this.bond.map(t => t.toUpperCase())}</Rspan>

Reload and play around:

image

3. Reusing Bonds

Right now we just have a single "user" of our this.bond, but actually Bonds can be used and reused as much as you want. Let's use this.bond to create a style for our <Rspan> in order to introduce colour depending on the text we have entered. Our <Rspan> will have a red colour if we have entered a simple number, and black in all other cases.

Replace your <Rspan> line with:

<Rspan style={this.bond.map(t => t.match(/^[0-9]+$/) ? {color: 'red'} : {color: 'black'})}>
	{this.bond.map(t => t.toUpperCase())}
</Rspan>

The code is fairly simple: To get the style of the <Rspan>, we map our this.bond to one of {color: 'red'} or {color: 'black'} depending on whether it matches the regular expression /^[0-9]+$/.

Our bond is now being used twice in two different mappings, but all works as you would expect:

image

While the above might have been clear enough, the astute reader might have wondered if perhaps there was a way of DRYing the {color: ...}, changing this:

this.bond.map(t => t.match(/^[0-9]+$/) ? {color: 'red'} : {color: 'black'})

...into this:

{color: this.bond.map(t => t.match(/^[0-9]+$/) ? 'red' : 'black')}

The two are rather different, of course. Whereas the former is undeniably a Bond, the latter is a simple object which happens to have a Bond as one of its values. In fact, the latter does work. For convenience, reactive values are able to be recognised not just directly, but also when they are within Arrays or the values of Object fields. For efficiency, this only work up to one level deep. Any which are further into the object structure will be ignored (however there are APIs for altering how deep you wish Bonds to be recognised).

4. Combining Bonds

So far we have only used a single Bond for marking up our <Rspan>. What if we want to use several? That works, too.

We could initialise another Bond in the constructor, make another InputBond input field and use them both in the span contents, but that would be a bit samey. Instead, we'll use one of the built-in Bonds: the TimeBond.

To begin, import TimeBond from oo7, alongside Bond:

import {Bond, TimeBond} from 'oo7';

Then introduce it in the constructor:

constructor() {
	super();
	this.bond = new Bond();
	this.time = new TimeBond();
}

Finally, let's use it in our <Rspan>. There are several ways of combining the values of multiple Bonds into a single expression. For now we'll use the simplest: Bond.all. This function allows you to provide a number of expressions and will evaluate to an array of all such expressions. This means that Bond.all([this.bond, this.time]) will be a composite Bond, whose value is a JS array, the first item being the contents of our text field and the second being the time (as a number).

Try it. Replace the <Rspan> with this new one:

{% raw %} <Rspan style={{ color: this.bond.map(t => t.match(/^[0-9]+$/) ? 'red' : 'black') }}> {Bond.all([this.bond, this.time])}

{% endraw %}

Type an appropriate message and you'll end up with:

![image](https://cloud.githubusercontent.com/assets/138296/22697591/e779b292-ed1f-11e6-8beb-2ff654e6ac02.png)

It will automatically update as the time changes. Since time changes infinitely fast, a speed which would be expensive to try to process, we take a pragmatic solution and only update the `TimeBond` once per second.

We can make it slightly nicer by first formatting the time:

{% raw %}
```jsx
<Rspan style={{ color: this.bond.map(t => t.match(/^[0-9]+$/) ? 'red' : 'black') }}>
	{Bond.all([this.bond, this.time]).map(([msg, t]) => `${new Date(t)}: ${msg}`)}
</Rspan>

{% endraw %}

image

In order to make the code more readable, we're going to pull out the color processing and the time formatting into separate functions. Our code looks like this now:

import React from 'react';
import {Bond, TimeBond} from 'oo7';
import {Rspan} from 'oo7-react';
import {InputBond} from 'parity-reactive-ui';

const computeColor = t => t.match(/^[0-9]+$/) ? {color: 'red'} : {color: 'black'}
const format = ([msg, t]) => `${new Date(t)}: ${msg}`

export class App extends React.Component {
	constructor() {
		super();
		this.bond = new Bond();
		this.time = new TimeBond();
	}
	render() {
		return (
			<div>
				<InputBond
					bond={this.bond}
					placeholder="Go ahead and type some text"
				/>
				<Rspan
					style={this.bond.map(computeColor)}
				>
					{Bond.all([this.bond, this.time]).map(format)}
				</Rspan>
			</div>
		);
	}
}

That should have given you a good introduction to the concept of the Bond and how it can be used within simple React objects. Next up we'll look at the Parity Universal Bond API.


3rd Part ⟶