William Liu

React

Summary

What is React? React is a declarative JavaScript library for building user interfaces. We are able to build complex UIs from small, isolated pieces of code called ‘components’. These components tell us what we want to see on the screen. React handles the update and re-render of components.

Example

An example of a component is React.Component subclasses:

class ShoppingList extends React.Component {
  render() {
    return (
      <div className="shopping-list">
        <h1>Shopping List for {this.props.name}</h1>
        <ul>
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>
        </ul>
      </div>
    );
  }
}

// Example usage: <ShoppingList name="Mark" />

So what does this mean? ShoppingList is a React component class (akak React component type). A component takes in parameters (props, aka ‘properties’) and returns a hierarchy of views to display via the render method.

The render method returns a description of what you want to see on the screen. React takes this description and displays the result, which is a React element (lightweight description of what to render).

React uses a special syntax called JSX which makes these structures easier to write. The <div /> syntax is transformed at build time to React.createElement('div'). The example above turns into:

return React.createElement('div', {className: 'shopping-list'},
  React.createElement('h1', /* ... h1 children ... */),
  React.createElement('ul', /* ... ul children ... */)
);

Top-level API

https://reactjs.org/docs/react-api.html

If you load React from a <script> tag, then these top-level APIs are available on the React global.

#ES6 with npm
import React from 'react'

#ES5 with npm
var React = require('react')

Tools

Google Chrome Extensions has a ‘React Developer Tools’ extension With this, in Chrome DevTools, you can click on the ‘Components’ section to look at React Components.

JSX

JSX is the syntax extension to JavaScript. We use JSX with React to describe what the UI should look like. JSX produces React ‘elements’. It looks like this:

const element = <h1>Hello world!</h1>

The idea is that React separates concerns with loosely coupled units called components.

const name = 'Will'
const element = <h1>Hello {name}</h1>;

ReactDOM.render(
    element,
    document.getElementById('root')
);

You can put any type of JavaScript expressions inside the curly braces in JSX (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions)

Note: Recommend using the ‘Babel’ language definition for your editor for proper syntax highlighting For VSCode, that means installing the extension ‘vscode-language-babel’

Elements

Elements are the smallest building blocks for React apps. An element describes what you see on the screen. React DOM handles updating the DOM to match the React elements. React’s components are made up of a lot of elements.

const element = <h1>Hello world</h1>

We normally have a single ‘root’ DOM node because everything inside it will be managed by React DOM. To render a React element into a root DOM node, pass both to ReactDOM.render(), like:

# Say we have this in your HTML file
<div id="root"></div>

# call this to render element into DOM
const element = <h1>Hello world</h1>;
ReactDOM.render(element, document.getElementById('root'));

Updating a rendered element

React elements are immutable; once you create the elmenet, you cannot changes its children or attributes. The only way to update the UI is to create a new element and pass it to ReactDOM.render().

function tick() {
  const element = (
    <div>
      <h1>Hello world</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(element, document.getElementById('root'));
}

setInterval(tick, 1000);  # call the render every second

Even though we describe the whole UI tree each tick, React only updates the content that has changed (if any).

Components and Props

Components lets you split the UI into independent reusable pieces. Components can be defined as either classes or functions (with classes providing more features). To define a React component, you need to extend React.Component. For reference, the component API is here: https://reactjs.org/docs/react-component.html

Component (as class)

Here it is as a class

class Welcome extends React.Component {
  render() {
    return <h1>Hello {this.props.name}</h1>
  }
}

The only method you have to define in a React.Component sublcass is the render() method. The idea is that code reuse is primarily done through composition rather than inheritance.

Component (as function)

Here it is as a function (aka function components)

function Welcome(props) {
  return <h1>Hello {props.name}</h1>
}

This is a valid React component because it accepts a single ‘props’ (properties) object argument with data and returns a React element.

Rendering a Component

Elements can be:

See examples below:

# DOM tag
const element = <div />;

# user-defined component
const element = <Welcome name="Will" />;

When React sees an element representing a user-defined component, it passes JSX attributes and children to this component as a single object (aka ‘props’).

Example

Here we render a ‘Hello Will’ on the page:

function Welcome(props) {
  return <h1>Hello {props.name}</h1>;
}
const element = <Welcome name="Will" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

Here’s what is happening:

Note: Always start a component name with a capital letter. Starting with a lowercase letter means that this gets interpreted as a DOM tag instead of a component.

Components referring to components

Your components can refer to other components in their output. This gives us the same component abstraction for any level of deatil. Everything (a button, a form, a screen) are all expressed as components. For this example, we have an App component that renders multiple Welcome components:

function Welcome(props) {
  return <h1>Hello {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

State

You can convert a function into a component class by calling it a class, adding a render() method, and replacing props with this.props like so:

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello world</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

To add state, we add a class constructor that assigns the inital this.state

class Clock extends React.Component {
  constructor(props) {
    super(props);  /* You always want to call the base constructor with props) */
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello world</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

Instead of updating based off a time (e.g. every 1 second update DOM), we use lifecyle methods (see ‘Lifecycle’):

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    /* runs after the component output has been rendered to the DOM */
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    /*  runs tear down code */
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

So what’s going on?

1 The <Clock /> component is passed to ReactDOM.render() where React calls the constructor method. 2 The constructor method initializes this.state with an object (the current time) 3 React calls the Clock component’s render() method and React updates the DOM to match the render output 4 When the Clock output is inserted into the DOM, React calls the componentDidMount() lifecycle method 5 Inside this method, Clock asks the browser to set up a timer to call the component’s tick() method once a second 6 Each second the browser calls the tick() method and the Clock component schedules a UI update by calling setState() with an object containing the current time. The setState() call lets React know that the state has changed so it calls render() method again to see what should be drawn (using the new this.state.date) 7 If the Clock component is removed from the DOM, then React calls componentWillUnmount() method

Rules about State

Some rules about state:

Set state with setState()

State updates may be asynchronous

For example:

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

State updates are merged

When you call setState(), React merges the object you provide into the current state.

Data Flows Down

Parent and child components do not know (and do not need to know) if a component is stateful or stateless This is why state is called local or encapsulated. A component can pass its state down as props to its child components like this:

<FormattedDate date={this.state.date} />

The FormattedDate component can receive the date in its props and wouldn’t know whether it came from the Clock’s state, fromt he Clock’s props, or was typed by hand.

function FormattedDate(props) {
  return <h2>It is {props.date.toLocaleTimeString()}</h2>;
}

Lifecycle

Each component has several ‘lifecycle methods’ that you can override to run code at particular times in the process. The Lifecycle Cheat sheet is here: https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/ The main lifecycle methods are for:

Mounting

These methods are called in this order when a component is created and inserted into the DOM:

Updating

An update is caused by changes to props or state. These methods are called in the following order when a component is being re-rendered:

Unmounting

This method is called when a component is being removed from the DOM:

render() method

The render() method is the only required method in a class component. When called, it should examine this.props and this.state and return one of the following types:

Renders should not modify component state and should return the same results each time it’s invoked

Handling Events

Handling events with React elements is similar to handling events on DOM elements.

Say we have this HTML

<button onClick="activateLasers()">
  Activate Lasers
</button>

In React, this would be:

<button onClick="{activateLasers}>
  Activate Lasers
</button>

Prevent Default behavior

In React, to prevent default behavior, you cannot return false. Instead, you have to call preventDefault explicitly. In HTML this might look like:

<a href="#" onclick="console.log('The link was clicked'); return false">
  Click me
</a>

In React this would be:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked');
  }

  return (
    <a href="#" onClick{handleClick}>
      Click me
    </a>
  );
}

Adding Listeners

With React, you do not need to call addEventListener to add listeners to a DOM element after it is created. Instead, provide a listener when the element is initially rendered. A common pattern is for an event handler to be a method on the class. For example, the Toggle component renders a button that lets the user toggle between ‘ON’ and ‘OFF’ states:

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // this binding is necessary to make 'this' work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

Passing additional arguments to event handlers

You can pass extra parameters to an event handler. Either of these would work to pass in an additional id param.

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

These two are the same, just one uses arrow functions and the other uses Function.prototype.bind. The e argument representing the React event will be passed as a second argument after the ID. With an arrow function, we explicitly pass e. With bind, any additional arguments are automatically forwarded.

Conditional Rendering

In React, you can create distinct components that encapsulate behavior you need. You can then render only some of them depending on the state of your application. Conditionals in React are the same as conditions in Javascript, use an if or a conditional (ternary) operator to create elements representing the current state (and let React update the UI).

function UserGreeting(props) {
  return <h1>Welcome back!</h1>
}

function GuestGreeting(props) {
  return <h1>Please sign up</h1>
}

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

ReactDOM.render(
  <Greeting isLoggedIn={false} />,
  document.getElementById('root')
);