Generators: Rick Astley and the Sequence of Fibonacci
Front-End Full-Stack WebOn the internet, there are two things that are totally played out—Rick Astley and the Fibonnaci sequence. But finally, in one blog post, they...
In my previous post, we created a simple UI component with Facebook’s React library. Now, we’ll create data flows between multiple components.
Let’s build an app that takes the input from a textfield and displays it as rot13.
We’ll structure the app like so:
And, the components we’ll build are:
It may not be obvious right now, but the container is essential. Every React component must return a single element. In order for us to display both the Input and the Output components, they need to be inside another component.
We’ll start with our boilerplate HTML, making sure to include the libraries for React and for the JSX Transformer. Remember, when you work with JSX, you need to include the /** @jsx React.DOM */
directive and specify the script’s type as text/jsx
.
For each component, we pass in a “spec” object that has a render function.
<!DOCTYPE html>
<html>
<head>
<script src="http://fb.me/react-0.10.0.js"></script>
<script src="http://fb.me/JSXTransformer-0.10.0.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/jsx">
/** @jsx React.DOM */
var InputComponent = React.createClass({
render: function () {
}
});
var OutputComponent = React.createClass({
render: function () {
}
});
var AppComponent = React.createClass({
render: function () {
}
});
React.renderComponent(
<AppComponent />,
document.getElementById('container')
);
</script>
</body>
</html>
Next, we need the render function of our components to return values.
var InputComponent = React.createClass({
render: function () {
return (
<input></input>
);
}
});
var OutputComponent = React.createClass({
render: function () {
return (
<div></div>
);
}
});
var AppComponent = React.createClass({
render: function () {
return (
<div>
<InputComponent />
<OutputComponent />
</div>
);
}
});
React.renderComponent(
<AppComponent />,
document.getElementById('container')
);
Notice that the AppComponent
returns a <div>
with the InputComponent
and OutputComponents
nested inside of them. When the AppComponent
’s render
function is called, the render
functions of the InputComponent
and OutputComponent
are called as well. This produces a DOM tree roughly equivalent to:
<div>
<input />
<div></div>
</div>
We need a way to get data from one component to another. React has a simple, but sophisticated, system for moving data around. Here is how we might get the static text “Ni hao, React” from our AppComponent
to our OutputComponent
:
var InputComponent = React.createClass({
render: function () {
return (
<input></input>
);
}
});
var OutputComponent = React.createClass({
render: function () {
return (
<div>{ this.props.value }</div>
);
}
});
var AppComponent = React.createClass({
render: function () {
return (
<div>
<InputComponent />
<OutputComponent value="I know kung fu" />
</div>
);
}
});
React.renderComponent(
<AppComponent />,
document.getElementById('container')
);
Our AppComponent
provides a value
attribute to the OutputComponent
. Then, the OutputComponent
accesses this attribute as a property of this.props
.
This is an example of React’s one-way data flow. Every component has access to a props
object, and those properties are added to this props
object by the parent component. The property names can be any valid JavaScript variable name, and the value can be any valid JavaScript value or expression.
Here, we named the property “value,” but it could have easily been “stringToDisplay” or “outputText.”
To access the value in the OutputComponent
’s JSX, you wrap it in single curly braces. This tells the JSX Transformer to evaluate whatever JavaScript expression is inside those curly braces.
The values passed from a parent component to a child component do not have to be static. The first step is the let the AppComponent
maintain its own store of dynamic data. A state
variable, like the props
variable, is available to every component. But state
is the data that is owned by a component, as opposed to being provided by a parent component.
Let’s update the code so that the AppComponent
sets up an initial state
object for holding data, and passes some of that data to the OutputComponent
.
var AppComponent = React.createClass({
getInitialState: function () {
return {
value: "I know kung fu!!!!",
}
},
render: function () {
return (
<div>
<InputComponent />
<OutputComponent value={ this.state.value } />
</div>
);
}
});
React.renderComponent(
<AppComponent />,
document.getElementById('container')
);
This code has the equivalent outcome as the previous version, but reveals a little bit more about how to use React effectively. The getInitialState
function is called automatically and sets up the state
object for a component. We can return any object, and are specifying an object literal for the sake of simplicity.
To pass some of our AppComponent
’s state data to the OutputComponent
, we use attributes, as we did before. But this time, instead of a static string value, we use the expression this.state.value
, wrapped in single curly braces.
Note that we do not wrap { this.state.value }
in quotes. If we did that, it would be passing a string, and not the actual JavaScript value we intended.
It’s time to complete our data flow by connecting our InputComponent
’s input element to our AppContainer
’s state. First, we will watch for the change
event on the input.
var InputComponent = React.createClass({
_changeHandler: function (event) {
console.log(event.target.value);
},
render: function () {
return (
<input
onChange={ this._changeHandler }
></input>
);
}
});
Again, we make use of attributes to declare how our components should interact. Technically, the <input>
element is a React component, and we are specifying its onChange
attribute. We pass it this._changeHanlder
as the value, and we declare a _changeHandler
property as part of our component’s spec.
onChange
is an attribute that is baked into React. You can find a list of supported event attribute names in the Event System documentation on the React site.
Alternatively, _changeHandler
is a name that we made up arbitrarily. One convention that is popular in the React community is to prefix your custom functions with an underscore. This is a good way to communicate that this is a function that you have declared, and is not one of React’s built in component functions.
We define _changeHandler
just like any other DOM event handler callback; it should accept a parameter for the event object. Currently, it is only logging the value of the input. To make it do something more interesting, we’ll need to make a couple of changes to the AppComponent
.
We will update AppComponent
by adding a function that updates its state, and we will pass a reference to this function to InputComponent
.
var AppComponent = React.createClass({
getInitialState: function () {
return {
value: "I know kung fu!!!!",
}
},
_updateValue: function (newValue) {
this.setState({
value: newValue
});
},
render: function () {
return (
<div>
<InputComponent sendChange={ this._updateValue }/>
<OutputComponent value={ this.state.value} />
</div>
);
}
});
We have created an _updateValue
function that calls the built in setState
component function. It accepts an object whose properties will overwrite the existing state. When you call setState
, you only have to specify the ones you want to update, even if your component’s state has many other properties.
We declare an attribute for InputComponent
named sendChange
and give it a reference to _updateValue
. InputComponent
now has access to this function reference in its props
object. Let’s use that to complete the data flow.
var InputComponent = React.createClass({
_changeHandler: function (event) {
this.props.sendChange(event.target.value);
},
render: function () {
return (
<input
onChange={ this._changeHandler }
></input>
);
}
});
Now, when you type into the input field, output updates immediately. Right now, it’s in plain text. Let’s fix that.
We need to add our rot13 function to the OutputComponent
and call it in our render
function.
var OutputComponent = React.createClass({
_rot13: function (s) {
return s.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);});
},
render: function () {
return (
<div>{ this._rot13(this.props.value) }</div>
);
}
});
Now, our output, though not anywhere near NSA-proof, is unreadable by normal humans. Hooray!
React Components act as neat little boxes that take some input and produce a piece of your UI. Each one has limited responsibilty, which makes it easier for you to reason about your app. While building this app, you learned how to pass data and function references using props
and state
. Then you made these data flows reactive via Event attributes. Next time, we’ll work on an even larger application and examine the benefits of React over other frameworks.
On the internet, there are two things that are totally played out—Rick Astley and the Fibonnaci sequence. But finally, in one blog post, they...
We’re excited to announce that we’ve made our HTML5 Apps with jQuery bootcamp even better. Our new Cross-Platform JavaScript Apps with HTML5 class is...
Facebook's React is a JavaScript library for building high performance User Interfaces. As they like to say, it's the just "V" in MVC. Better...