React

Resources for Learning

Leveling Up Your Game

Forcing a Re-render

Conditional Attributes

For just a single attribute, the easiest is to set it to null to prevent it from rendering. For some attributes, React is smart enough that you can use false instead of null.

<button disabled={isDisabled ? true : null}>Launch Missle</button>

Spread Operator Solution

When you have several attributes, this is a nice way to do it. Build an object how you like:

var inputProps = {
  value: 'foo',
  onChange: this.handleChange
};

if (condition) inputProps.disabled = true;

Render with spread, optionally passing other props also. Notice how the order below makes a difference.

  <input 
      value="this is overridden by inputProps" 
      {...inputProps} 
      onChange={overridesInputProps}
   />

ES6 Patterns

Stateless Functional Components

import React from 'react'
import PropTypes from 'prop-types';

let UserName = (props) => (
  <div>
    <p>{fullName}</p>
  </div>
)

UserName.propTypes = {
  fullName: PropTypes.string,
  date: PropTypes.number.isRequired
}

// You can specify default values for optional properties.
UserName.defaultProps = {
  fullName: 'George Washington'
}

export default UserName

React properties are allows passed into a component as a an object. In the above example you can see fullName is destructured from the props on line 3. Because fullName is inside curly braces, we know that it is getting destructured.

Standard Components

import React from 'react';
import PropTypes from 'prop-types';

class Greeting extends React.Component {
  constructor (props) {
    super();
    this.curTime = new Date().toLocaleTimeString();
  }

  render () {
    return <p>Hello, {this.props.userName}. Today, at {this.curTime}, it is {this.props.weather}.</>>;
  }
}

Greeting.propTypes = {
  userName: PropTypes.string,
  weather: PropTypes.string.isRequired
};

// You can specify defaults for the optional props.
Greeting.defaultProps = {
  userName: 'Earthling'
};

export default Greeting;

JSX Attributes to DOM Attributes

JSX Attributes to get rendered to the DOM if they are a valid attribute. You can force non-standard attributes to render by putting a dash in the name; best practice is to use data-...

export default (...props) => (
  <input
    type='checkbox'
    monsterFood='granola'
    data-uniqueId='Dracula'
  />
)

will render to the DOM as:
<input type="checkbox" data-uniqueId="Dracula">

The React component will still have a property called monsterFood.

Conditionals in JSX

JSX does not support if statements, but you can use Javascript's ternary and boolean operators.

if/else

<p>{style === 'IPA' ? 'Is bitter' : 'Is not bitter'}</p>

render when true

<p>{style === 'IPA' && 'Is bitter'}</p>

render when false

let studentIsAgeOfMajority = false;
<p>{!(studentIsAgeOfMajority) && 'Parent’s permission needed'}</p>

Is equivalent to:

let studentAge = 16;
<p>{(studentAge < 18) && 'Parent’s permission needed'}</p>

Events

To pass parameters to an event, see this post.

Basically, the component the captures the event must have a private function to handle the event. You attach the private function to the, say, onClick event. The private function then calls the function passed down via props.

Refs

Refs are kinda like specifying an ID on an element. You can then reach into the DOM (via ReactDOM.findDOMNode()) by the ref name. Example:

class RandomSpinner extends React.Component {
  constructor(props) {
    super(props);
    this.refCompass = React.createRef();
  }

  onSpin = () => {
    this.refCompass.current.blur(); // Use `current` to get the actual DOM element.
  }
  
  render () {
    return (
      <div ref={this.refCompass}>This is the compass elem.</>
    )
  }
}

Lifecycle Hooks

Initialization

  1. getDefaultProps
  2. getInitialState
  3. componentWillMount
    You may set state, but it will not trigger a re-render.

    This is a great place to hook-up event listeners. e.g.

    componentDidMount() {
      ReactDOM.findDOMNode(this).addEventListener('click', this.onClick);
    }
  4. render()
  5. componentDidMount
    Perform DOM interaction here.

State Changes

  1. shouldComponentUpdate (nextProps, nextState)
    This function must return a boolean value.
  2. componentWillUpdate (nextProps, nextState)
    Always called before render(), but not for the initial render. Do not call setState() here.
  3. render()
  4. componentDidUpdate (prevProps, prevState)
    You can perform DOM manipulations here.

Prop Changes

  1. componentWillReceiveProps (nextProps)
    This is a good place to call setState().
  2. shouldComponentUpdate (nextProps, nextState)
    This function must return a boolean value.
  3. componentWillUpdate (nextProps, nextState)
    Always called before render(), but not for the initial render. Do not call setState() here.
  4. render()
  5. componentDidUpdate (prevProps, prevState)
    You can perform DOM manipulations here.

Passing Data to Actions from an Event

ES5 Syntax

For one-off, quick-n-dirty solution you can use .bind() to pass one, or more, values.

this.clickHandler.bind(this, val1, val2, val3)

render: function() {
  let items = this.props.items;
  return (
  <ul>
  {() => {
  items.map((item) => {
  // bind the components onItemClick method
  // and use the bind syntax that prepends
  // arguments to attach the item argument
  let boundItemClick = this.onItemClick.bind(this, item);

  // Construct the onClick with our bound function
  return <li key={item.id} onClick={boundItemClick}>{item.title}</li>
  });
  }()};
  </ul>
  );
},

onItemClick: function(item, e) {
  console.log(item);
}

This could be all done in one line:

return <li onClick={this.onItemClick.bind(this, item)}> ... </li>

ES6 Syntax

The same thing can be done less verbosely with fat arrow functions.

return <li onClick={() => this.onItemClick(item)}> ... </li>

ES5 Patterns

If you must write native ES5 code, this style can be used.

Stateless Functional Components

function HelloWorld () {
  return (
    <div>Hello {this.props.name}</div>
  )
}

Standard Components

var React = require('react')
var ReactDOM = require('react-dom')
var HelloWorld = React.createClass({ // The createClass function actually creates a Component.
  render: function(){
    return (
      <div>
        Hello World!
      </div>
    )
  }
});
ReactDOM.render(<HelloWorld />, document.getElementById('app'));

Error Boundaries

The biggest gotcha is pulling out the error text into a location where you can use it.

import React from "react";

export default class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true, error: error.name + " " + error.message };
  }

  componentDidCatch(error, info) {
    // Use this to log errors.
    this.error = error.name + " " + error.message;
  }

  render() {
    if (this.state.hasError) {
      return (
        <React.Fragment>
          <h1>
            Something went wrong.{" "}
            <span role="img" aria-label="Sad face" style={{ fontSize: "2em" }}>
              😞
            </span>
          </h1>
          <p>Error: {this.state.error}</p>
        </React.Fragment>
      );
    }
    return this.props.children;
  }
}