Skip to content

Instantly share code, notes, and snippets.

@bknie1
Last active April 19, 2020 21:00
Show Gist options
  • Select an option

  • Save bknie1/3781caea4861247823c978c63f8fb37f to your computer and use it in GitHub Desktop.

Select an option

Save bknie1/3781caea4861247823c978c63f8fb37f to your computer and use it in GitHub Desktop.
React and Redux Guide

React and Redux Guide

This is guide to getting started with React and, later, Redux. it will use a Node.js, Express.js, and MongoDB back end, so familiarity with that tech is ideal.


Table of Contents

1. About React

Introduction to React

2. React, Components, and JSX

Getting Started

React Components

JSX

Standard React App Layout

Styling React


3. Props

Introduction to Props

Default Props


4. Create React App

Introduction to Create React App

Installing Create React App

Create React App Conventions

CSS and Assets in Create React App

Webpack

Importing and Exporting Modules


5. State

Introducing State

Initializing State

Setting State

State Versus Props


State Patterns

State As Props Design Pattern

Update Existing State

Mutating State

Designing State: Minimizing Components, Downward Data Flow


7. Events

Basic Events

Method Binding

8. Keys

React Keys


About React

TOC Shortcut

What is React?

It is a popular, powerful front-end framework and library developed and sponsored by Facebook. It started on the web but, thanks to React Native, we can use it on mobile. React has a very strong community of developers.

It makes it easy to make reusable "view components"; a combination of HTML, CSS, and JavaScript. These encapsulate logic and HTML into a class. It makes it easier to build modular applications.

Previously, we would separate HTML, CSS, and JavaScript into their own tasks. With React, we combine all three to create components, so it is important to consider them in tandem.

Why learn React?

Because Facebook is backing it, it is not likely to go away anytime soon. It is in demand. It is incredibly easy to create modular web applications.

React Framework vs. Library Discussion

It is hotly debated whether or not React is a framework, library, or both.

Frameworks require you to follow certain rules in order to produce a result. Libraries offer tools that enable developers to produce a result. React, at its base, is a library. However, when we introduce other tools like React Router and Redux, we create more of a framework because those common tools introduce rules that we must abide by.

Ultimately, React can be considered both a library and a framework depending on the stack.


Getting Started

TOC Shortcut

This section covers components, props, state, JSX, and the component lifecycle. Create React is a tool utilized elsewhere but, for the sake of creating a component for the first time, one could use this boilerplate and create "components/index.js":

Note: It is especially important that the index.js component be of type"text/jsx". Components are written in JSX format, a HTML-like format that is actually JavaScript. We use this to interpret React components in the DOM.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>First Component</title>
</head>
<body>
  <div id="root"></div>
  <script src="https://unpkg.com/react/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>

  <script src="https://unpkg.com/babel-standalone"></script>
  <script src="components/index.js" type="text/jsx"></script>

</script>
</body>
</html>

Dependencies

React Development

This is the core React package.

React DOM

This works in tandem with React Development. It is required for React work in the browser.

Babel

Babel is a transpiler that translates our modern JavaScript into ES 2015 and ES5 JavaScript so that it is compatible with older browsers.

React Components

TOC Shortcut

Components are the main building block of React. React describes components: "[they] let you split the UI into independent, reusable pieces, and think about each piece in isolation". One of the most challenging elements of React is determining what should be a component.

Components are:

  • The building blocks of React
  • Pieces of UI & View logic
  • Classes that know how to render themselves into HTML

Here's a pseudocode example of how components work in ES 2015 style JavaScript:

class Dog {
    constructor(name, color) {
        this.name = name;
        this.color = color;
    }
    
    render() {
        return '<p>${this.name}</p>';
    }
}

Each 'Dog' has a name and a color. It knows how to render itself in the DOM. Again, this is not real React, but it illustrates how React performs.

Creating Components

We can either declare Class Components or Function Components. There are uses for each.

Note: It is standard to capitalize the declared names of React components.

Class Components

  • The traditional React component
  • Write logic in a JavaScript class
  • Must include a 'render' method
  • Must 'extend' (inherit) the React.Component class

Here is a basic Component that renders as a child of 'div#root'.

class Hello extends React.Component {
	render() {
        // Can only return one element.
        // You may return one element that contains others, though.
		return (
            <div>
                <h1>Hello!</h1>
                <h3>How's it going?</h3>
            </div>
        );
	}
}

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

Function Components

  • Used for simpler, "dumb" components
  • Write logic in a JavaScript function
  • No render method needed; just returns the content/markup

Here is the same Component from the class Component example as a function Component:

function Hello {
    return (
        <div>
            <h1>Hello!</h1>
            <h3>How's it going?</h3>
        </div>
    );
}

What's the difference?

  • Both can accept 'props' and render content
  • Historically, function components couldn't use important features like:
    • State
    • Lifecycle Methods
  • With the introduction of 'Hooks' we can now write full-featured function components

JSX

TOC Shortcut

Depending on who you ask, JSX stands for JavaScript Syntax Extension or JavaScript Syntax XML. Allows us to type HTML-looking code in JavaScript. It allows us to combine our UI with our JavaScript logic.

Note: You do not need JSX to write React, but it is much uglier.

Basic Rules

JSX is more strict than HTML, elements must either:

  • Have an explicit closing tag ...
  • Be explicitly self-closed:

You cannot leave the '/' off or else it will syntax error.

How JSX works

JSX "fundamentally just provides syntactic sugar for the React.createElement(component, props, ...children) function". Babel will transpile our JSX into standard JavaScript.

If we have a component that returns some JSX:

<div id="jsxDemo">
  <section>
    <h1>Some picture</h1>
    <img src="image.png" />
  </section>
</div>

Babel will transpile this into:

React.createElement("div", {
  id: "jsxDemo"
}, React.createElement("section", null, 
    React.createElement("h1", null, "Some picture"), 
    React.createElement("img", {
        src: someImage
    })
  )
);

Again, JSX is not required to create React apps, but having to write nested React.createElement calls is far less readable and more difficult to manage.

Embedding JavaScript in JSX

Using curly braces {} we can embed JavaScript in our JSX:

render() {
    return (
        <section>
            <h1>Embedded JS: 2 * 4 = {2 * 4}</h1>
            <img src={getImageUrl()} /> // Quotes will be added
        </section>
    );
}

Conditionals in JSX

We can use our embedded JavaScript syntax to use conditional logic:

const getNum = () => {
	return Math.floor(Math.random() * 10) + 1;
};

class NumPicker extends React.Component {
	render() {
		let yesImgUrl = "someImage.gif"
		let num = getNum();
		return (
			<div>
				<h2>Your number is {num}</h2>
				<p>{num === 7 ? 'Lucky!' : 'An acceptable number.'}</p>
				{num === 7 ? <img src={yesImgUrl} /> : null}
			</div>
		);
	}
}

Looping in JSX

It is common to use array.map() to output loops in JSX. Below, for each message, we create a list item. Then, an unsorted list is returned. This pattern is very common.

class Messages extends React.Component {
	render() {
		const messages = [
			{id: 1, text: "Greetings!"},
			{id: 2, text: "Goodbye!"}
		];
		
		return (
			<ul>
				{ messages.map(m => <li>{m.text}</li>) }
			</ul>
		)
	}
}

Standard React App Layout

TOC Shortcut

Components

Components should be stored in separate files; one ComponentName.js file per Component.

Top Level Component

We have one Component that renders all of the other components. This is traditionally named 'App.js'. It combines all of our other components into a single element. It is the one Component we render into the DOM. This way, code readers know exactly where to start. This is usually the only thing rendered in index.js.

It is important to load scripts in order. We load index.js last and all other components before it.

Styling React

TOC Shortcut

Styling with React is similar to without React; we can use our own stylesheet or import a framework. A key difference is that it is standard to use CSS class names that match the Component names. If I have a Component, 'Machine', I would have a matching CSS class '.Machine'; complete with a capital M.

However, we cannot simply add this class to our Component using the 'class' attribute. Instead, we use 'className', because class is a reserved keyword, and we are working in a JavaScript (JSX) context.

To reiterate:

  • Class names match Component names and are capitalized
  • To add a class to a Component, use the className attribute, because 'class' is a reserved keyword
.Machine {
	border: 2px solid black;
}

Below, in Machine Component's render:

return (
	<div className="Machine"> // 'className' instead of 'class'
		<span>
			{props.s1} {props.s2} {props.s3}
		</span>
		<p>{areSame ? 'You win!' : ''}</p>
	</div>
);

Note: The 'for' attribute for the label element is also reserved (for loops). We would need to use 'htmlFor' instead. Be careful regarding standard HTML attribute names and conflicts with JavaScript.

We can also use inline CSS styles but 'style' takes a JavaScript object:

return (
	// We now use an object to pass inline styles
	const styles = {
		border: 1px solid black
	}

	<div style={styles}>
		<span>
			{props.s1} {props.s2} {props.s3}
		</span>
		<p>{areSame ? 'You win!' : ''}</p>
	</div>
);

We can also use JavaScript to set style properties. We have to wrap an object in an object and use camel case for our CSS properties e.g. nstead of font-size, we write fontSize.

return (
	// Note the double curly braces and camelCase
	<div style={{fontSize: '24px', backgroundColor: 'purple'}}> 
		<span>
			{props.s1} {props.s2} {props.s3}
		</span>
		<p>{areSame ? 'You win!' : ''}</p>
	</div>
);

---

## Props

### Introduction to Props

[TOC Shortcut](#table-of-contents)

Props, a.k.a. Properties, make components more reusable through configuration and customization. This is similar to the relationship between class Objects and their constructors; we use constructors to fill out classes, which serve as templates.

Properties are **immutable**; they do not change. If we want to interact with the data in props we need to do so elsewhere. We cannot assign new values to it.

Here, we have a Component, HelloWithProps, that contains data in this.props, which is inherited from React.Component. In the App's Component render syntax, we assign the 'to' and 'from' properties using HTML-like attribute syntax.

```js
class HelloWithProps extends React.Component {
	render() {
		return (
			// this.props contains all property information
			<div>
				<h1>Hello {this.props.to}!</h1>
				<h3>How's it going?</h3>
				<h4>From {this.props.from}</h4>
			</div>
		);
	}
}

class App extends React.Component {
	render() {
		return (
			<section>
				// Properties are assigned like HTML attributes
				<HelloWithProps to="Ringo" from="Paul" albumYear={1969} areBeatles={true} hobbies={['drumming', 'guitar']}/>
			</section>
		);
	}
}

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

Default Props

TOC Shortcut

To add default props to a Component we create a static function called defaultProps.

Note: It must be called defaultProps.

Here, if we only pass a recipient (props.to), we default to an anonymous sender (props.from).

class Hello extends React.Component {
	static defaultProps = {
		from: "Anonymous"
	}
	
	render() {
		return(
			<p>
				Hi {this.props.to} from {this.props.from}
			</p>
		);
	}
}

Create React App

Introduction to Create React App

TOC Shortcut

React is a front-end library; you don't need server-side stuff. You can get react.js and react-dom.js from a CDN. You can transpile JSX in the browser at runtime. However, we can enhance our process.

Create-React-App is a utility script that:

  • Creates a skeleton React project
  • Sets it up so that JS files are run through Babel automatically
  • Lets us use super-modern JavaScript features/idoms
  • Makes testing and deployment much easier

It is an official tool that is associated with React and Facebook.

Installing Create React App

TOC Shortcut

Create React App can be installed with Node.js's npm or npx:

React: Create React App Guide

npm is used to manage packages but does not execute packages. npx is used to execute node packages:

npx create-react-app my-app

Even though create-react-app is not installed, it will get and execute the latest version of the package. This package is not something we need to execute more than once, so npx is most appropriate.

We can start the server with the command:

npm start

Create React App Conventions

TOC Shortcut

Each React component goes into a separate file

  • src/Car.js for a 'Car' Component
  • src/House.js for a 'House' Component

The name of the file should match the Component name. Components are capitalized.

All Components should extend Component

The Component class is imported from React. It exports the 'Component' class as the default object. We can import and extend Component using two different styles:

import React, { Component } from 'react';
// OR
import React from 'react';

class someComponent extends Component { ... }
// OR
class someComponent extends React.Component { ... }

App in App.js

The CRA skeleton assumes the top object is named App and it is exported from App.js. This is attached to the #root element. It is best to maintain this as there is no reason to change it and it is the standard.

CSS and Assets in Create React App

To include images ancd CSS you can import them in JavaScript files:

import logo from "./logo.svg";
import "./App.css";

// ...

<img src={logo} className="App-logo" alt="logo" />

CSS

It is common to make a CSS file for each Component e.g. House.css for a House Component. We import it at the top of the Component script. CRA will automatically load the CSS.

There is also a conventional naming hierarchy. The class name should match the Component name and the CSS file. Child items use kebab-case to specify child styles:

<div className="House">
	<p className="House-title>{title}</p>
	<p className="House-address>{address}</p>
</div>

Images

Images are typically stored in the 'src' folder with the components. However, if we have many images, we can store them in a subfolder. Load them where needed and use the imported name where path should go:

import puppy from "./puppy.jpg";

<img src={puppy} alt="puppy" />

Webpack

TOC Shortcut

CRA is built on webpack. It's a JavaScript utility that:

  • Enables module importing/exporting
  • Packages up all CSS/images/JS into a single file for the browser
    • Reduces the number of HTTP requests needed
  • Hot Reloading: When you change a source file it automatically reloads
    • It only reloads relevant files
  • Enables easy testing and deployment

CRA does all the hard work for us.

Importing and Exporting Modules

TOC Shortcut

Modules

CRA uses ES2015 'modules'. This is a newer, standardized version of Node's require(). You can use this to export and import classes/data/functions between JavaScript files.

Exporting and Importing a Single Function

Imagine we have two files: index.js and helper.js. Here, we only choose to export the helpful() function out of this file:

// helper.js
const helpful = () => {
	console.log("Import me!");
}

// Note: No parentheses, because we don't want to call it
export default helpful; 

We import the file, by name, with ./ notation, because it is a file. If we do not use ./ notation, it means we are attempting to import a node module from the node_modules folder.

// index.js
import React from 'react'; // Node Package
import ReactDOM from 'react-dom'; // Node Package

// Note: The name does not have to match the file or function.
import helpfulFunction from "./helper"; // Not a Node Package

helpfulFunction();
Exporting and Importing Multiple Functions

We often want to export multiple things from a module. We can use object notation to export multiple functions.

// helpers.js
const helpful = () => {
	// ...
}

const mostHelpful = () {
	// ...
}

const reallyHelpful = () {
	// ...
}

// We use object syntax to export each function
export default helpful;
export {mostHelpful, reallyHelpful};

Our import function must compliment this object export function. This time, the name does matter, because we can pick and choose what exports we want to use in the current file.

// index.js
import {mostHelpful, reallyHelpful} from "./helpers";

export vs. export default

If we choose to import a module that has both export default and export, but do not specify functions, we will only import the default function. If we specify, we can import more of the functions from the file.

Conventionally, default exports are used when there is a "most likely thing" to export. For example, in a React component file, it is common to have the Component be the default export.

You never need to make a default export, but it can be helpful to indicate the most important functionality in a module.

Using the previous example we can import all three functions using this syntax:

import helpful, {reallyHelpful, mostHelpful} from "./helpers";

State

Introducing State

TOC Shortcut

In any sufficient advanced web app, the user interface has to be stateful; there is information that is likely to change. State is designed to constantly change in response to events. Every time an action is taken the state can change.

State Tracking

There are two types of things state can track on the front end:

UI Logic

The changing state of the interface.

  • Logged in users see a different screen than logged out hsers
  • Clicking Edit Profile opens a modal
  • Sections of a site expand and collapse
Business Logic

The changing state of the data.

  • Inbox messages
  • Events registered for
  • Account status

Vanilla and jQuery State Tracking

With vanilla JavaScript in jQuery, we relied heavily on style and visibility to demonstrate state. Elements would be shown, hidden, colored, disabled, and more.

React State Tracking

Internal data is Component specific. Each Component is responsible for its own State. Data changes over time.

With React, we can reload part of the DOM to reflect state changes. React is "smart" enough to know what has changed and what has not.

State is an instance attribute on a Component. It is always a plain-old JavaScript object (POJO), since you will want to keep track of several keys and values.

// What is the current state of my Component?
console.log(this.state);

/*
Would output:
{
	playerName: "Whiskey",
	score: 100
}
*/

Initializing State

TOC Shortcut

Unlike props, we have to initialize state on the components we want to use state.

If your Component is stateless, you can omit the constructor function. Otherwise, we need to use a constructor. State should be initialized as soon as the Component is created. We set it in the constructor function.

Constructor takes a single argument: props. 'super(props)' is necessary boilerplate that can sometimes be omitted. 'super' is a reference to the Component constructor.

Inside instance methods, one may refer to 'this.state' just like 'this.props'.

class Game extends Component {
	constructor(props) {
		super(props);
		this.state = {
			// Values we want to track:
			score: 0,
			gameOver: false
	};
	
	render() {
		return(
			<div>
				<h3>Your score is {this.state.score}</h3>
			</div>
		);
	}
}

export default Game;

Alternative Syntax

There is an alternate, experimental state initialization syntax. However, the other syntax is not part of JavaScript or the JavaScript standard; it relies on Babel. It omits the constructor and this.state syntax.

Babel will take the invalid JavaScript code and convert it into code that includes the constructor and this.state syntax.

class Game extends Component {
	state = {
		// Values we want to track:
		score: 0,
		gameOver: false
	};
	
	render() {
		return(
			<div>
				<h3>Your score is {this.state.score}</h3>
			</div>
		);
	}
}

export default Game;

super() vs. super(props)

Super allows us to access parent properties. Even if we choose not to use state, constructor() is part of Component, and we need to call super(); to indicate that we want to use Component's constructor. If we don't, we get an error.

Also, if we want to use the derived class's props, we need to pass it to super so that the constructor can interact with those props.

Setting State

TOC Shortcut

In React, you must never directly manipulate the state. Accessing the state on a component and making changes outside of the constructor is very poor design.

Incorrectly Setting State

class Game extends Component {
	state = {
		// Values we want to track:
		score: 0,
		gameOver: false
	};
	
	this.state.score += 1; // THIS IS BAD
	
	render() {
		return(
			<div>
				<h3>Your score is {this.state.score}</h3>
			</div>
		);
	}
}

export default Game;

Correctly Setting State

We can use this.setState(), a build-in React method, to change a component's state. We can use this to call any instance method except the constructor. You can only call setState on a Component that has been mounted.

  • It takes an object describing the state changes
  • It patches the state object. Unspecified keys do not change
  • It's asynchronous! The component state will eventually update

Components will re-render when you change their state.

We do not set state manually because of all of these React specific features.

this.setState({playerName: "Brandon", score: 0});

Here, given a Component with a prop, maxNum, we can change the displayed number every second. When the state is updated, it re-renders the Component:

// Given the inclusion: <Rando maxNum={7} />

import React, { Component } from 'react';

export default class Rando extends Component {
	constructor(props) {
		super(props);
		this.state = { num: 0 };
		this.makeTimer();
	}

	makeTimer() {
		setInterval(() => {
			let rand = Math.floor(Math.random() * this.props.maxNum);
			this.setState({ num: rand });
		}, 1000);
	}

	render() {
		return <div className="Rando">{this.state.num}</div>;
	}
}

State Versus Props

TOC Shortcut

After Components, State and Props are the most important things in React.

Term Structure Mutable Purpose
state POJO {} Yes Stores changing component data
props POJO {} No Stores component configuration

State Patterns

State As Props Design Pattern

TOC Shortcut

A common pattern is a stateful, "smart" parent component that passes down its state values as props to stateless, "dumb" child components.

class CounterParent extends Component {
	constructor(props) {
		super(props);
		this.state = {count: 5};
	}
	
	render() {
		// Passing down parent state as a prop to the child component
		return (
			<div>
				<CounterChild count={this.state.count} />
			</div>
		);
	}
}

This idea is g eneralized in React as "downward data flow". It means that components get simpler as you go down the component hierarchy, and parents tend to be more stateful than their children.

Update Existing State

TOC Shortcut

Thus far, it has been common practice to overwrite values, but we often want to update existing values. We can do this by using the current state, operating on it, and setting it.

Incorrect Example: Updating State directly

Imagine we have two buttons that call these functions.

You may consider the following in order to accomplish this, but it is incorrect:

// INCORRECT EXAMPLE

singleIncrementation() {
	this.setState({ score: this.state.score + 1 });
}

tripleIncrementation() {
	this.setState({ score: this.state.score + 1 });
	this.setState({ score: this.state.score + 1 });
	this.setState({ score: this.state.score + 1 });
}

Why is it incorrect?

Remember, the setState operation is asynchronous, so if we are depending on current State information, that State may or may not be up to date. Also, React will batch calls to setState together for performance reasons.

Correct Example: Updating State with a Callback

Instead, we can use a callback function to assure State is up to date for our operation. Instead of passing an object, pass a callback with the current state as a parameter. The callback returns an object representing the new state:

// CORRECT EXAMPLE

singleIncrementation() {
	this.setState(st => {
		return { score: this.state.score + 1 }
	});
}

tripleIncrementation() {
	this.setState(st => {
		return { score: this.state.score + 1 }
	});
	
		this.setState(st => {
		return { score: this.state.score + 1 }
	});
	
		this.setState(st => {
		return { score: this.state.score + 1 }
	});
}

functional SetState Pattern

Because we pass callback functions in order to update State, it makes sense to create functions that can be passed instead of writing them inline. This is called functional setState. We abstract away State updates to specific function calls:

incrementScore(st) {
	return { score: st.score + 1 }
}

singleIncrementation() {
	this.setState(st => incrementScore(st));
}

tripleIncrementation() {
	this.setState(st => incrementScore(st));
	this.setState(st => incrementScore(st));
	this.setState(st => incrementScore(st));
}

This pattern is very popular in Redux.

Mutating State

TOC Shortcut

We need to be able to manage state updates for mutable data structures. Up until now, examples have primarily focused on primitives like numbers and strings.

Incorrect Example: Mutating State

// INCORRECT EXAMPLE
completeTodo(id) {
	const theTodo = this.state.todos.find(t => t.id === id);
	theTodo.done = true;
	
	this.setState({ todos: this.state.todos });
}

Mutating nested data structures in your state can cause problems with React.

Correct Example: Mutating State

Instead, we should make a new copy of the data structure. We can use any pure function to do this. A pure function's return value is based only on its inputs and has no other dependencies or effects on the overall program:

completeTodo(id) {
	const newTodos = this.state.todos.map(todo => {
		if(todo.id === id) {
			// The 'spread' op unpacks the existing array into a brand new array
			return {...todo, done: true}; // Return a matching todo with the updated state
		}
		
		return todo; // Don't manipulate other todos
	});

	this.setState({
		todos: newTodos // We use our copy, with the updated info, to update the state
	});
}

Immutable State

Immutable state means that there is an old state object and a new state object. We copy the old state, add updates, and return our new state as the update. This is a good habit for React application building but is a necessity for Redux.

Immutable State Update Tools

We can use Array's pure functions like:

  • .map(): Creates a copy of an array with modifications, if we like
  • .filter(): Returns an array with filtered values from the source array
  • .reduce(): Returns a single accumulator data structure using the source array

It is worth the O(n) space/time required to make a copy because we want to ensure that our app has the most up to date state. It also reduces the chances of difficult bugs.

Designing State

TOC Shortcut

There are best practices for modeling state and designing components. It takes a lot of practice to learn React design:

  • What should be a Component?
  • What part of the Component should manage State?

Minimize Your State

We want to put as little data in the state as possible. We can ask simple questions to determine if some data should be a state or a prop:

"Does x change?" If not x should not be part of the state; it should be a prop.

"Is x already captured by some other value y in state or props?" If so, derive it from there instead.

Example: Person

Name

A person's name does not change often. This should be a prop.

Birthday

A birthday does not change. This should be a prop.

Age

You may think that age changes, and it does, but age can be derived from Birthday, and so we can calculate this elsewhere. It should be a prop.

Mood

A person's mood does change often. This is suitable as a State property.

Downward Data Flow

State should live on the Parent

We want to support the "downward data flow" philosophy of React. That is, Parent Components should be "smarter" or more stateful than their "dumber", stateless child Components. The former relies heavily on State, the latter on Props.

If a current Component is Stateless, find out what is rendering it. That is where the State lives.


Events

Basic Events

TOC Shortcut

Click Event (onClick)

State most commonly changes in direct response to some kind of event. In React, every JSX element has built-in attributes representing every kind of browser event. They are camel-cased, such as onClick, and take callback functions as event listeners.

<button onClick={function(e) { alert("Clicked!"); } }>
	Click Me
</button>

We can also bind specific functions, but we must also bind 'this' to those functions so that, when we call 'this', we know what 'this' is referring to, because we specifically said what it should refer to:

import React, { Component } from 'react';

export default class Rando extends Component {
	constructor(props) {
		super(props);
		this.state = { num: 0 };
		// IMPORTANT:
		// When we refer to 'this', we're specifically saying
		// use this instance of this component.
		this.setRandomNumber = this.setRandomNumber.bind(this);
	}

	getRandomNumber() {
		return Math.floor(Math.random() * this.props.maxNum);
	}

	setRandomNumber(e) {
		this.setState({ num: this.getRandomNumber() });
	}

	render() {
		return (
			<div className="Rando">
				<div>{this.state.num}</div>
				// Assign the function, but don't call it with ()
				<button onClick={this.setRandomNumber}>Random Number</button>
			</div>
		);
	}
}
Alternate Bind Syntax

If we omit the bind() syntax, and intead use an arrow function, Babel will bind the function to 'this' automatically. Below, getRandomNumber() has been converted to this. We no longer need a constructor:

import React, { Component } from 'react';

export default class Rando extends Component {
  state = { num: 0 };

	setRandomNumber() {
		return Math.floor(Math.random() * this.props.maxNum);
	}

	// This will bind getRandomNumber to 'this' automatically
	getRandomNumber = (e) => {
		this.setState({ num: this.getRandomNumber() });
	}

	render() {
		return (
			<div className="Rando">
				<div>{this.state.num}</div>
				// Assign the function, but don't call it with ()
				<button onClick={this.setRandomNumber}>Random Number</button>
			</div>
		);
	}
}

Mousing Over (onMouseOver, etc)

Similar to vanilla JavaScript, we can handle mouse events. We can also use various other mouse events: Mouse enter, on mouse over, on mouse exit, etc.

Form Events

Form events, like onSubmit, etc., are used for form interaction and submission.

Keyboard Events

Similar to vanilla JavaScript, React allows us to use keyboard events:

  • onKeyDown
  • onKeyUp
  • onKeyPress

Other Events

Clipboard events, pointer events, and much more are available to React, just as they are available to vanilla JavaScript.

Method Binding

TOC Shortcut

They keyword 'this' relies on context. When we create Component methods we need to specifiy what 'this' is.

Incorrect Example

This is a method inside a component. When we refer to 'this' we receive an error because 'this' was never defined.

printHaiku() {
  let { haikus } = this.props; // An array, named haikus, in props
  let i = Math.floor(Math.random() * haikus.length);
  console.log(haikus(i));
}

So how do we make sure 'this' refers to the Component? We need to fix the binding.

Fixing the Binding

In order to use 'this' in the method we need to define what 'this' is referring to. We can do this with one of three tools:

Binding in the Constructor

This is what is used in earlier examples. We bind 'this' to this method so that this method knows what 'this' refers to when 'this' is used in the method.

This method of binding is a little less clean, as, for every method call, we need to add an additional line to our constructor. However, it has many benefits, and may be the preferred way of binding.

  • You only need to bind once
  • It is more performant
  • The intent is clear

Here is a familiar example:

constructor() {
  this.printHaikus = this.printHaikus.bind(this);
}
Binding Inline

When we specify how an event is handled we can include an inline binding. This is very explicit. However, if we need to pass 'this.printHaiku' to multiple components, this will not work. Also, a new function is created on every render, which is not as resource intensive as one may think, but it is less organized. At scale, it can create performance issues.

// Instead of just assigning the function, we include bind(this)
<div className="Haiku" onClick={this.printHaiku.bind(this)}>
  // Some content
</div>
Binding with an Arrow Function

We call an anonymous arrow function that calls, not just assigns, the method we want.

  • No mention of bind
  • Intent is less clear
  • What if you need to pass the function to multiple components?
  • New function created every render
// Note: We do call the function with '()'
<div className="Haiku" onClick={ () => this.printHaiku() }>
  // Some content
</div>
Binding with Class Properties (Experimental as of 2020)

We can use ES 2015 function declaration syntax to bind this function. On transpile, Babel will handle the actual binding by moving it to the constructor. As this becomes polished it may become standardized. It is common in many React applications.

Here is an example:

handleClick = () => {
  console.log(`This is ${this}`);
}

Binding with Arguments

TOC Shortcut

In the previous section, methods were not bound with arguments. But what happens when we need to pass arguments as function parameters? We can accomplish this by passing the argument in our bind:

We can use the inline or arrow function bind approaches. The key is that we pass in our arguments; after 'this' if it's an inline bind, or as an arrow function argument:

onClick={this.changeColor.bind(this, c)} 
// OR
onClick={ () => changeColor(e) }

Complete Example

class ButtonList extends Component {

colors = ['red', 'green', 'blue'];

constructor(props) {
  super(props);
  this.state = { color: 'white' }; // Initial
}

changeColor(newColor) {
  this.setState({ color: newColor });
}

render() {
  return (
    // For every color, create a button with that color
    // That button will change the background color of the page
    <div className="ButtonList" style={{ backgroundColor: this.state.color }} >
      {this.props.colors.map{c => {
        const colorObj = { backgroundColor: c };
        return <button 
          onClick={this.changeColor.bind(this, c)} 
          style={colorObj}>Click on me!</button>;
      })}
    </div>
  );
}

Passing Methods to Child Components

TOC Shortcut

This is a very common pattern in React. Children are often not stateful but need to tell parents to change state. How do we send data "back up" to a parent component?

How data flows

  1. A parent component defines a function
  2. The function is passed as a prop to a child component
  3. The child component invokes the prop
  4. The parent function is called, usually setting a new state
  5. The parent component is re-rendered, and so are the children

Where to bind

  • The higher the better; don't bind in the child component if possible
  • If you need a parameter, pass it down to the child as a prop, then bind in parent and child
  • Avoid inline arrow functions / binding if possible
  • No need to bind in the constructor and make an inline function
Inline Prop Function

We can pass an inline reference to the function and an argument using a prop. However, every time we re-render we create a new function:

// Every NumberItem gets a reference to remove with a specific argument
let nums = this.state.nums.map(n => {
  <NumberItem value={n} remove={() => this.remove(n) } /> 
});

return (
  <div>
    <ul>{nums}</ul>
  </div>
)
Constructor Prop Function

Instead, we can use our constructor to bind 'this'. But, we need to include a helper that can pass remove the argument. This is the preferred method.

constructor(props) {
  super(props);
  this.state = { nums: [1, 2, 3, 4, 5] };
  this.remove = this.remove.bind(this);
}

remove(num) {
  // Returns all but matching num
  let this.setState(st => {
      st.nums.filter(n => n !== num)
    }
   ));
}

// Every NumberItem gets a reference to remove with a specific argument
let nums = this.state.nums.map(n => {
  <NumberItem value={n} remove={this.remove} /> 
});

return (
  <div>
    <ul>{nums}</ul>
  </div>
)

In the child, we add a handleRemove function that will call the remove function, from props, with our argument. However, like with any function that requires 'this', we need to bind 'this' to handleRemove in the constructor.

class BetterNumberItem extends Component {
  constructor(props) {
    super(props);
    this.handleRemove = this.handleRemove.bind(this);
  }

  handleRemove(e) {
    this.props.remove(this.props.value);
  }

  render() {
    return(
      <li>
          {this.props.value}
          <button onClick={this.handleRemove}>X</button>
        </li>
      );
    }
}

Parent-Child Method Naming

It is good to follow some pattern when naming methods. A recommended method is using the action() and handleAction() pattern, where action is a method name.


Keys

React Keys

TOC Shortcut

When mapping over data and returning components, you get a warning about keys for list items. key is a special string attribute to include wehn creating lists of elements.

  • Keys should be unique: Usually UID's in a data set
  • Keys help React identify which items have changed, are added, or are removed
  • Keys should be given to repeated elements to provide a stable identity

Note: If you do not have a stable, unique ID for items, you may use the iteration index as a key. However, there may still be issues. We can use packages like shortid and uuid to create unique identifiers.

const numbers = [1, 2, 3, 4, 5];

const listItems = numbers.map(n => 
  <li key={n.toString()}>
    {n}
  </li>
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment