React is a UI Library in Javascript. Created by the folks at Facebook, it helps with creating webcomponents, but better than the webcomponents implemented in HTML5. React allows you to create composable view components, utilizing a unidirectional data flow, or one-way data flow. This differs to Angular that has a data binding components, which is two directional between controller and view.
Note: I call elements components, but now they're really called elements. Take that in consideration when I use the word component.
The render call takes a function. In the return statement, only one node is allowed. This means the following is not allowed.
render: function() {
return (
<b>Hello</b>
<h1>World</h1>
)
}
You will need to wrap your node in a tag in order for it to render, or React will spit an error at you. The following example shows a correct render function.
render: function() {
return (
<div>
<b>Hello</b>
<h1>World</h1>
</div>
)
}
The following code is taken from this Egghead tutorial. It shows how you can set default properties with getDefaultProps
attribute. You can also set the propTypes
with the following primitive types:
- React.PropTypes.array
- React.PropTypes.bool
- React.PropTypes.func
- React.PropTypes.number
- React.PropTypes.object
- React.PropTypes.string
Besides primitives, there can be nodes and elements, instances of classes, enums, unions, and oneOfTypes. There are built in validators like isRequired
, as shown below, and you can build custom validators. More on properties here.
var App = React.createClass({
getDefaultProps:function(){
return {
txt: 'this is a default prop',
cat: 0
}
},
propTypes: {
txt: React.PropTypes.string,
cat: React.PropTypes.number.isRequired
},
render:function(){
var txt = this.props.txt
return (
<div>
<b>BOLD</b>
<h1>{txt}</h1>
</div>
);
}
});
React.render(<App cat={5} />, document.body);
Brackets are used to show the variable from the component onto the view. In this case, {txt}
will show the txt
property.
Props are to be passed in as static values or methods.
The following code sample is taken from Egghead.
var App = React.createClass({
getInitialState:function(){
return {
txt: 'the state txt',
id: 0
}
},
update: function(e){
this.setState({txt: e.target.value});
},
render:function(){
return (
<div>
<input type="text" onChange={this.update} />
<h1>{this.state.txt}</h1>
</div>
);
}
});
React.render(<App txt="this is the txt prop" />, document.body);
States are meant to be maintained and updated by our component. Unlike Props, these variables and function may be dynamic (or hold state).
Use the ES6 Spread operator to transfer properties of one component to another.
return <h1 {...this.props} id="message">Glorious</h1>
Owner Ownee Relationship is when a component uses another react component. This denotes the parent-child relationship. For example, if we have a Widget component used by our app component, the Widget is the child. See more at the Egghead tutorial.
We use Refs
to reference our child components. See the example below.
var App = React.createClass({
getInitialState:function(){
return {
red: 0,
green: 0,
blue: 0
}
},
update: function(){
this.setState({
red:this.refs.red.refs.inp.getDOMNode().value,
green:this.refs.green.refs.inp.getDOMNode().value,
blue:this.refs.blue.refs.inp.getDOMNode().value
});
},
render:function(){
return (
<div>
<Slider ref="red" update={this.update} />
<label>{this.state.red}</label>
<Slider ref="green" update={this.update} />
<label>{this.state.green}</label>
<Slider ref="blue" update={this.update} />
<label>{this.state.blue}</label>
</div>
);
}
});
var Slider = React.createClass({
render:function(){
return (
<div>
<input ref="inp" type="range" min="0" max="255" onChange={this.props.update} />
</div>
);
}
});
React.render(<App />, document.body);
Note that if we wrap our child component in a div, we can add a ref
in our input, call that from our parent chaining refs inp
, and see the change on our app.
Child Properties: this.props.children
will give us whatever was passed into the component. Much like Angular's transclude
in directives.
Pass functionality into multiple components. Mixins are objects.
var ReactMixin = {
getInitialState:function(){
return {count:0};
},
componentWillMount:function(){
console.log('will mount!');
},
incrementCount: function(){
this.setState({count: this.state.count+1})
}
}
You can add the mixin for use like the following example.
var Button = React.createClass({
mixins:[ReactMixin],
render:function(){
return <button onClick={this.incrementCount}>{this.props.txt} - {this.state.count}</button>
}
});
Calling those attributes to the React component that are the same as the mixin will merge both of those together.
If you're not using the JSX transformer, you've got to wrap your React elements into factories for it to render on the page.
React.render(React.createFactory(APP)(), document.getElementById('exampleNoJSX'));
Mounting is when the component is introduced to the DOM.
Mounting
- componentWillMount - Mounts the component to the DOM
- componentDidMount - Verifies component is mounted to DOM. Runs after componentWillMount
Unmounting
- componentWillUnmount - Unmounts component from the DOM
All three of these are attributes with callback functions.
- componentWillReceiveProps - takes nextVal as an input. We can run a callback to update the properties
- shouldComponentUpdate - when returns true, will update the component
- componentDidUpdate - Verifies component was updated. prevProps and prevStates are the callback's arguments
A tenant of computer programming is DRY, or don't repeat yourself. Composable components are components that are reusable. In the following example from EggHead, we can notice the logic for NumInput should know everything about itself, just like App. This allows for separation of concerns between components.
var App = React.createClass({
getInitialState:function(){
return {
red: 0
}
},
update: function(){
this.setState({
red: React.findDOMNode(this.refs.red.refs.inp).value
});
},
render:function(){
return (
<div>
<NumInput
ref="red"
min={0}
max={255}
step={0.01}
val={+this.state.red}
type="number"
label="Red"
update={this.update} />
</div>
);
}
});
var NumInput = React.createClass({
propTypes: {
min: React.PropTypes.number,
max: React.PropTypes.number,
step: React.PropTypes.number,
val: React.PropTypes.number,
label: React.PropTypes.string,
update: React.PropTypes.func.isRequired,
type: React.PropTypes.oneOf(['number', 'range'])
},
getDefaultProps:function(){
return {
min: 0,
max: 0,
step: 1,
val: 0,
label: '',
type: 'range'
}
},
render:function(){
var label = this.props.label !== '' ?
<label>{this.props.label} {this.props.val}</label> : ''
return (
<div>
<input
ref="inp"
type={this.props.type}
min={this.props.min}
max={this.props.max}
step={this.props.step}
defaultValue={this.props.val}
onChange={this.props.update} />
{label}
</div>
);
}
});
React.render(<App />, document.body);
Passing in data, typically in the form of JSON, we can create a child component and render each collection item to render the way you want. In the Egghead example, they render a person row by creating a map in its parent render function. This is much different than the .erb
for
examples, for loops in PHP, ng-repeat
in Angular. This is the React way of doing things.
The following are notes from Egghead on how the JSX transformer works for rendering components. More on the transformer on the React Documentation. There are additional tools on the documentation website for Babel support for ES6 and ES7.
- found in supported HTML tags
<div></div>
- !found in supported HTML tags
<App></App>
- self closing tags
<div />
- multiple nodes == returning multiple functions :(
<div></div>
<a></a>
- single node == returning single function :)
<div>
<a></a>
</div>
- first argument == component props,functions, etc.
<div>
<a href="#"></a>
</div>
- additional arguments == children
<div>
<a href="#">this is the text</a>
</div>
- React will not render unknown attributes to the browser without data-*
<div notrendered="x" data-rendered="x">
<a href="#">this is the text</a>
</div>
- interpreted
<div notrendered="x" data-rendered="x">
<a href="#" onClick={this.update}>
{/* this is a comment */}
this is the text
</a>
</div>
- if else is no good in JSX syntax, use a ternary expression
<div notrendered="x" data-rendered="x">
<a href="#" onClick={this.update}>this is the text</a>
{i>1 ? 'More than one' : 'One'}
{i>1 && 'More than one'}
{/* this is a comment */}
</a>
</div>
- inline styles
var myStyle={
backgroundColor:'#000',
height:10 //no need for 'px'
}
<div style={myStyle} notrendered="x" data-rendered="x">
<a href="#" onClick={this.update}>this.props.children</a>
{i>1 ? 'More than one' : 'One'}
{i>1 && 'More than one'}
{/* this is a comment */}
</a>
</div>
In production, the JSX transformer is very big and quite slow. Precompile the JSX code when pushing changes to production. You'll want to split the development version with build version, much like we do for the build system in other Javascript frameworks. react-tools
is one that is recommended by Egghead.
TODO
- Look into other build systems (Webpack should be good, but also Browserify, etc.) - And see if it plays well with react-tools
$ npm install react-tools
Change the react library to include addons. And example is changing the CDN link as follows:
https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react.js -> https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react-with-addons.js
It's like a two-way binding helper. Instead of writing update functions to update each element, we can use ReactLink to bind the view's state. Egghead example.
To use, add the mixin React.addons.LinkedStateMixin
.
A Chrome extension that gives you a view of React components, which shows more than the elements tab in Chrome. It shows properties, state, event listeners, and a lot more.
React-Bootstrap is a library for creating React components with Bootstrap components. Currently for Bootstrap 3 (anticipated Bootstrap 4 support when it is release). Under heavy development before the stable 1.0 release of React.
Since React is just an elements library, an external library is needed for creating views. The React library has a router component called react-router-component
. Two methods exist for Router that are of much importance.
var Router = require('react-router-component');
var Locations = Router.Locations;
var Location = Router.Location;
Within the Locations tag, you can include your different paths. In your paths, you can include symbols to be used in place of ids, slugs, etc.
<Locations>
<Location path="/" handler={/* Your Composite Component here */} />
<Location path="/cart" handler={/* Your Composite Component here */} />
<Location path="/item/:item" handler={/* Your Composite Component here */} />
</Locations>
In the code above, which will fall within the render
attribute of your React component, you'll include some Location
tags in order to add routes.
React-router
is an alternative to Facebook's react-route-component. It handles nested routes, declarative redirect and notFound routes, and much more. The project is highly active and the API will break a lot before the stable 1.0 release of React.
Looking at how other projects are handled, it seems this needs to be on the top-level of the application, if you're going to use the flux architecture.
React can be used in conjunction with other frameworks like Backbone, Angular and Ember. It appears that you can utilize components to encompass jQuery libraries and other Javascript libraries such as d3. In fact, when creating charts, it might be useful to use d3 as a component that can interact with other components, which could create very rich and dynamic applications. For more on d3 and Angular integration with React, check out this Egghead Video. Also check out Shirley Wu's post on utilizing d3, React, and a little bit of flux. The app can be found here. The source code can be found here.
Flux is an architecture that allows for using React as a full application framework. It is not a tool. Facebook created a dispatcher tool that can totally be utilized.
Images Credit: scotch.io
- Action - helper functions
- Action Creators
- Constants
- Dispatcher - broadcasts payloads to callbacks
- Store - App stores and logic
- View - React Components
- API - Remote APIs
Takes incoming view actions from the view and designates the action to be handled by the dispatcher. This is where the logic of what goes into the dispatcher will go, like if you're creating a shopping cart and you want to add an item, you will want to pass the item in. This Egghead video goes into more depth.
// remember to add the dispatcher and constants
var AppActions = {
addItem: function(item) {
AppDispatcher.handleViewAction({
actionType: AppConstants.ADD_ITEM,
item: item
});
}
};
module.exports = AppActions;
This is the open source dispatcher tool that Facebook has released to creating Dispatchers. Include it in your code. The dispatcher converts actions and figures out which store it should go. The business logic of what happens to that action resides in the store.
- Dispatcher.prototype.register - Registers a callback for stores on what to do with action
- Dispatcher.prototype.dispatch - assigns action a designation for stores
- Object.assign is an extension tool to include additional properties to objects. Much like
$.extend
in jQuery.
var assign = require('react/lib/Object.assign');
- While not a tool, it may be easier to describe the actions in a Constants file in its own folder.
- Eventemitter will be useful, from Node, for Stores.
There is where most of the business logic is. In the Egghead example, there is an AppStore
that holds the logic for what to do with the cart. The cart can add items, remove items, increase items, and decrease items. For different "models", you'll want to have different stores.
Many times we hear these views called controller views. This refers to the parent React element and all of its child elements. The logic of this controller is held within the children of the parent element. For the most part, this views box is just a React component. With Flux architecture, we don't actually have to place the business logic within the view as it's being maintained by the store (usually).
These element views will have some handler that will use the AppActions
functions so that it can make changes down the flux architecture, resulting in some change in the view. Here's an example of an AddToCart
element from the Egghead Github Repository from their react flux example.
var AddToCart = React.createClass({
handler: function(){
AppActions.addItem(this.props.item)
},
render:function(){
return <button onClick={this.handler}>Add To Cart</button>
}
});
Project organization becomes key here. It may be useful here to create different folder for different composite components (the parent and all of its children components). In the Egghead example of an App store, they split their components into distinct components.
- cart
- catalog
- header
- product
As said earlier, you can think of each of these folders as models. Each folder will generally contain one parent, although in some cases, you may have helper/template components as well. Depending on how the project is set-up, you'll also want a main app that has all of the routes. More on that in the routes section.
As mentioned before, constants can help maintain your actions as you do not want to remember what those are every time you're creating the actions or stores. It should be kept it its own folder.
Mixins, as mentioned in the React components section, can be useful to keep in its own folder.
At the end of this scotch article, there's a nice summary of what each of these components do.
Following along this Egghead video, they're using Gulp in conjunction with the following packages:
Gulp is a streaming service, much like node, in that it takes streams and you pipe them to some output, typically some file. Browserify allows for compiling your build to assets and extensions such as reactify allow for further transformations. Reactify transforms JSX code into production-ready javascript. Vinyl source stream grabs the source and pipes it into a stream rather than Browserify that outputs as strings.
https://reactjs.co This is the official free online convention and tutorial book for React.JS Developers. React is not only the View (in MVC) anymore. ReactJS For Dummies: Why & How to Learn React Redux, the Right Way.