React Programming

Misc

  • Remember that your component’s render() is a paint() function, it gets called every time some change (probably state) occurs
  • There is a PropTypes.shape.  See http://stackoverflow.com/questions/26923042/how-do-you-validate-the-proptypes-of-a-nested-object-in-reactjs
  • import React, {Proptypes} from 'react'  means Assign the default to React, additionally take React.PropTypes and assign it to PropTypes
  • PropTypes.arrayOf:  notice lower case A
  • Don’t make this mistake:   const {data} = this.props.data when you really meant  const {data} = this.props
  • Getting the child nodes:    propTypes: { children: PropTypes.node }
  • Frequent error:  typing getinitialState (getInitialState is correct)

Fundamental Principles of React

  • “declare your UI in the render method of your components, and React will automatically ensure the DOM matches what you declared.”
  • “React builds an in-memory representation of the components you render, and then syncs this representation to the DOM. As you update components, React will update the in-memory representation, and then make the least amount of changes to the DOM possible.”

Boilerplate Examples

React.createClass boilerplate

Note, createClass() is probably something you will see in out-of-date example code.  Modern versions of react will throw this warning when you use it:  “Accessing createClass via the main React package is deprecated, and will be removed in React v16.0. Use a plain JavaScript class instead.

const myComponent = React.createClass({

        propTypes: {                                    // lower case ‘p’

                myProp: PropTypes.string      // upper case ‘p’

        },

        getDefaultProps() {

                return {

     

                };

          },

        getInitialState() {

                return {  }

        },

        handleClick(e) {   // you can add local methods this way. Use this.handleClick to access

        },

        render(

                return (

                        <someTags onClick={ this.handleClick(e) }>stuff</someTags>

                )

        )

})

extends React.Component boilerplate version 1

class Contacts extends React.Component {

   constructor(props) {

      super(props);   // if this is your only line in constructor(), do not need constructor at all

      this.state = {     // instead of getInitialState()

      };

   }

   render() {

      return (

         <div onClick={ this.handleClick.bind(this) }></div>

      );

   }

}

Contacts.propTypes = {    // instead of getInitialProps()

};

Contacts.defaultProps = {   // instead of getDefaultProps()

};

extends React.Component boilerplate

class Contacts extends React.Component {

        Contacts.propTypes = {

};

Contacts.defaultProps = {

};

Non stateful components using React.component or React.createClass

When you have no need to manage state, this is the way to go.

const Contacts = ({foo, bar}) => (

        <div>Stuff to return</div>

)

Contacts.propTypes = {foo: PropTypes.string, bar; PropTypes.string}

Contact.defaultProps = {foo: ‘foo’, bar: ‘bar’}

export default Contacts

Misc Tips

Use deconstructive assignment to make code easier to read

    const {data} = this.props

  const {activeTabIndex} = this.state

Abbreviated way of setting a property

This is probably an ES6 thing.

This:

  selectTabIndex(activeTabIndex) {

    this.setState({

      activeTabIndex

    })

  }

…is the same way as saying this:

selectTabIndex(activeTabIndex) {

  this.setState({

    activeTabIndex: activeTabIndex

  })

}

React handlers must be functions

This is wrong, unless selectTabIndex returns a function:

        <Tabs onClick={this.selectTabIndex(index)}></Tabs>

…what was probably meant was:

        <Tabs onClick={() => this.selectTabIndex(index)}></Tabs>

The refs property in components

ref as a string:

        <myComponent ref=“someName”></myComponent>

…you can get a reference to the component with:

        var myThing = this.refs.someName

ref as a function:

        <myComponent ref={(ref) => this.myThing = ref} ></myComponent>

…now it is available as part of the object.

How would you use?  for focusing an element:

        this.myThing.focus()

Or to get its value:

        this.myThing.value

…instead of getting the value from the event arg passed in the myThing call.

        

Moving state from a Component to its parent

See React Training, 04_PropsVsState

  • In the parent component, you have a state that used to be in the child
  • Parent component now passes the value of the state as a property (use same name to be less confusing)
  • The parent passes an event handler that modifies the state, as a property to the child (let’s call it onActivate).  This handler takes an arg
  • The child defines its own handler for onClick, which takes an arg.  This handler then calls this.props.onActivate(arg)
  • I like to think of this as “tunneling” the state of the parent component through a property of the child component.

Controlled Input field

  • value vs defaultValue attributes of <input>:  ikmportant difference
  • Uncontrolled:  In React, defaultValue is the old fashioned way of setting the <input>’s value.  When you type in the <input>, it will echo your characters
  • Controlled:  this means “controlled by a state variable”.  Characters only echo when you go through a handler to work with the state variable.

Here the <input> will reflect the myInput state variable, but ignore typing in the field:

      <input value={this.state.myInput} type=“text” />

Here the typing will show up:

      handleChange(event) {

            this.setState({myInput: event.target.value})

      }

      <input value={this.state.myInput} type=“text” onChange={this.handleChange} />

Two ways of reading the value of an <input>

This:

      handleChange(event) {

            console.log(event.target.value)

      }

      <input type=“text” onChange={this.handleChange}

…or this:

      import { findDOMNode } from 'react-dom'

      handleChange(event) {

            console.log(findDOM(this.refs.myInput).value

      }

      <input ref=“myInput” type=“text” onChange={this.handleChange}

Submitting a form in bulk (‘transactional form’)

      import serializeForm from 'form-serialize'

      handleSubmit(event) {

            event.preventDefault()

            const data = serializeForm(event.target, { hash: true })

            //    do something with data

      }

      <form onSubmit={this.handleSubmit}>

            <input name=“someField” type="text" />

      </form>

Links

http://ricostacruz.com/cheatsheets/react.html

http://stackoverflow.com/questions/33655534/difference-between-declarative-and-imperative-in-react-js

PropTypes values

array,bool,func,number,object,string,symbol,node,element

What features does JSX add?

“Each tag, like <div />, is transformed into a call to React.createElement(). Any attributes become props of the instantiated component. Attributes can be strings like foo='hello', or they can be interpolated JavaScript expressions when wrapped in curly braces as in bar={baz} (which would set the bar prop to the variable baz).”

The “this” problem in HTML Event Handling

Best explanation: http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html

Supposer your render() has this:

      <button type='button' onClick={function() { this.setState({count: this.state.count + 1})}}>

        Click HERE to increment: {this.state.count}

      </button>

  • If you are using React.createClass() then this is no problem
  • But you are probably extending React.Component, the way you should, so your console will print an uncaught TypeError: Cannot read property 'setState' of null.  

Recall that this is a shortcut reference for the invoking object.  The invoking object is the HTML, not our React class.  So inside the anonymous function, this is undefined.  However, we have the power to bind our component’s this to the definition of the anonymous function:

      <button type='button' onClick={function() { this.setState({count: this.state.count + 1})}.bind(this)}>

        Click HERE to increment: {this.state.count}

      </button>

This code is getting hard to read.  Let’s make the code a little more real-world.  In our React component, the broken code looks like this:

        clickHandler:  function()  { this.setState({count: count + 1}) },

…and the HTML like this:

     <button type='button' onClick={this.clickHandler}>

        Click HERE to increment: {this.state.count}

      </button>

(Note:  just because the call to this.setState has been moved a little closer to the React class code doesn’t make this any more “available”.  clickHandler is still evaluating this at the time of the function call)

        

We can fix it in one of two ways:

  1. In the HTML:  onClick={this.clickHandler.bind(this}}
  2. In our component’s constructor:  this.clickHandler = this.clickHandler.bind(this)  Basically you are overwriting the clickHandler field with a redefinition of clickHandler.

(Note:  Javascript will not let you define a class field like this:

        clickHandler:  function()  { this.setState({count: count + 1}) }.bind(this),

…although that would be nifty.)

Fat Arrows to the rescue. But we’ve wasted a lot of time fussing over nothing!  If your javascript environment is using Babel’s stage 2 preset, just define your functions with fat arrows.  (See https://babeljs.io/learn-es2015/)  “Unlike functions, arrows share the same lexical this as their surrounding code.”

So given the Babel feature is present, you can do this:

     clickHandler: () => this.setState({count: count + 1})

…and you won’t need that bind() in the constructor any more.

Or if you want to go back to primitive inline anonymous functions:

      <button type='button' onClick={() => this.setState({count: this.state.count + 1})}>

        Click HERE to increment: {this.state.count}

      </button>

And also, there is :: syntax.  Say you’re back to using functions instead of Babel-supported fat arrows.  The solution where we bound this in the constructor can look like this:

        this.clickHandler = ::this.clickHandler

…or, the call to the method in the HTML can look like this:

        onClick={::this.clickHandler}

(Note:  I wasn’t able to get :: to work in jsFiddle, so this needs to be tested elsewhere)

What is the React Component lifecycle?

Mounting Cycle

  • constructor(object props): The component class is instantiated. The parameters to the constructor are the element's initial props, as specified by the parent element. You can optionally specify an initial state for the element by assigning an object to this.state. At this point, no native UI has been rendered yet for this element.
  • getDefaultProps():
  • getInitialState():
  • componentWillMount(): This method is invoked only once, before rendering occurs for the first time. At this point, there is still no native UI rendered for this element.  Setting the state in this phase will not trigger a re-rendering.  DOM interactions can happen in this phase.
  • render(): The render method must return a React Element to render (or null, to render nothing).  Neither props nor state should should be modified inside this function.   No DOM interactions should happen here.
  • componentDidMount(): This method is invoked only once, after rendering occurs for the first time. At this point, the native UI for this element has finished rendering, and may be accessed through this.refs for direct manipulation. If you need to make async API calls or execute delayed code with setTimeout, that should generally be done in this method.

Updating Cycle

  • componentWillReceiveProps(object nextProps):  The parent of this component has passed a new set of props. This component will re-render.
  • shouldComponentUpdate(object nextProps, object nextState): Based on the next values of props and state, a component may decide to re-render or not to re-render. The base class's implementation of this method always returns true (the component should re-render). For optimization, override this method and check if either props or state have been modified, e.g. run an equality test of each key/value in these objects. Returning false will prevent the render method from being called.
  • componentWillUpdate(object nextProps, object nextState):  This method is invoked, after the decision has been made to re-render. You may not call this.setState() here, since an update is already in progress.
  • render() :  This method is called, assuming shouldComponentUpdate returned true.
  • componentDidUpdate(object prevProps, object prevState):  Invoked after re-rendering occurs. At this point, the native UI for this component has been updated to reflect the React Element returned from the render() method.    DOM interactions can happen in this phase.

Write simple React apps in jsFiddle

Add these resources:

  1. https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.js
  2. https://npmcdn.com/react@15.6.1/dist/react-with-addons.js
  3. https://npmcdn.com/react-dom@15.6.1/dist/react-dom.js

In the upper right of the Javascript pane, click on the button and:

  1. Set Javascript 1.7
  2. Set Load Type to “no wrap in <head>”

your HTML needs a <script> include of:

The two principle libraries become available in the React and ReactDOM variables in the Javascript pane.

See https://www.reactenlightenment.com/react-basic-setup/3.4.html

Writing Bog-simple React Boilerplate

These are my plnkrs

http://plnkr.co/edit/VyC9SwPaQCj0BnWjH9hs:  One file, no Babel, no ES6, no jsx harmony, no imports, no requires

http://plnkr.co/edit/Mde4Vo3BnLlkpNOZmcYT: like above but the React code is in external file

What is Babel’s role in React?

  • Babel creates source maps so when you debug your code, you see your own ES6/ES2015 code rather than the code transpired into ES5
  • Babel 6 does not do ES6/ES2015 and React transformations by default, you have to have a .babelrc file containing: {  "presets": ["es2015", "react"]}
  • a “preset” in Babel is a “collection of plugins”.  For example, everything needed for Es6/ES2015 support is in the package babel-preset-es2015.  For React, the preset is babel-preset-react.
  • “stages” in Babel are levels of support for new features migrating into ES6/ES2015.  Stage-0 (aka babel-preset-stage-0) is the most experimental stage, consisting of items that aren’t guaranteed to survive.  stage 1 items will probably make it into release.  Nobody explains what’s in these presets (!), but stage 1 is recommended.
  • Babel CLI (babel-cli) is a tool for transpiling the code through the command line.
  • Don’t modern browsers like Chrome already support ES6/ES2015?  They do support many but not all features, so you’re taking too big of a risk by not using Babel to insure 100% support.

Tip


Links marked with arrow icons ( or ) will open in a new tab.

You will see this message only once.