Skip to content

Instantly share code, notes, and snippets.

@jermspeaks
Last active August 29, 2016 17:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jermspeaks/7e66b3a7515bb96fbc95 to your computer and use it in GitHub Desktop.
Save jermspeaks/7e66b3a7515bb96fbc95 to your computer and use it in GitHub Desktop.
My endeavors to learn react.js

Learning React JS

What is React

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.

Elements of React Elements

Note: I call elements components, but now they're really called elements. Take that in consideration when I use the word component.

Render

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>
		)
	}

Properties

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.

States

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).

Transfer Properties

Use the ES6 Spread operator to transfer properties of one component to another.

return <h1 {...this.props} id="message">Glorious</h1>

Composite Components

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.

Mixins

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.

Factories

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'));

Component Lifecycle

Mounting

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.

Updating

  • 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

Composable Components

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);

Dynamically Generated Components

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.

JSX

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

Add-ons and Third-Party Libraries

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

ReactLink

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.

React DevTools

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

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.

Routing

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.

Additional Notes

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

What is Flux

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.

Different Components of Flux

Images Credit: scotch.io

Flux Architecture Process

  • Action - helper functions
    • Action Creators
    • Constants
  • Dispatcher - broadcasts payloads to callbacks
  • Store - App stores and logic
  • View - React Components
  • API - Remote APIs

Action

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;

Dispatcher

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.

Flux Dispatcher

  • Dispatcher.prototype.register - Registers a callback for stores on what to do with action
  • Dispatcher.prototype.dispatch - assigns action a designation for stores

Other Tools

  • 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.

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.

Flux Dispatcher with Stores

Views

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>
  }
});

Flux Store to Views

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.

Other Useful Items

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.

Wrapping Up

At the end of this scotch article, there's a nice summary of what each of these components do.

Flux Process Descriptions

Setting up your environment

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.

@przeor
Copy link

przeor commented Aug 29, 2016

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment