Abstract

A thorough introduction to React and its professional use for production applications

Table of Contents

02 - Thinking and Understanding React Components

Components

Is a reusable piece of your site. Basically able to reuse tags throughout your site. They’re not custom tags, even though they’ll look like that in the react inspector, they’re react components. The components pull on data and whenever it changes then it gets updated.

03 - Creating our first component

04 - Writing HTML with JSX

Writing HTML in javascript is a pain though that pain has been eased slightly with the introduction of javascript templates. JSX makes it easier by being able to write HTML/React components without the use of HTML. It would look like:

<p className="hey">Hello</p>

There is an alternative way to write the html:

React.createElement('p', {className: "hey"}, "Hello")

The above makes one want to claw their eyes out especially when you have to nest create elements:

React.createElement('p', {className: "hey"}, React.createElement('p', {className: "cool"}, "Yoyo"))

Javascript’s automatic semicolon insertion is why you use the pair of parentheses: ()

Rendering and returning sibling elements

From a render method you can only ever return one element. Meaning that you can have multiple elements but there must be a root or parent element (you can’t return siblings):

<div className="parent"> 
	<p className="child1"></p>
	<p className="child2"></p>
</div>

However, in React 16.2 you can return siblings if you wrap them in a React.Fragment tag. It renders out to nothing and removes the need for needless dummy divs.

<React.Fragment>
	<p className="child1"></p>
	<p className="child2"></p>
</React.Fragment>

Soon blank tags may be allowed to achieve the same goal as React.Fragment:

<>
	<p className="child1"></p>
	<p className="child2"></p>
</>

Commenting in JSX

In order to comment in JSX, you need to use {} that basically tells the compiler that we’re going to be writing in Javascript now. There are no regular html comments.

{/* my comment */}

Also, realize that you currently can’t add a expression/comment before the html that is returned. Otherwise it will give a very vague error such as: Syntax Error: unexpected token , expected ,. This will fail:

{/* my comment */}
<div className="parent"> 
	<p className="child1"></p>
	<p className="child2"></p>
</div>

This will not:

<div className="parent"> 
	{/* my comment */}
	<p className="child1"></p>
	<p className="child2"></p>
</div>

05 - Loading CSS Into Our React Application

Componetized CSS

There’s two different ways that you can handle the CSS. The first is to globally add it to your base HTML page. The second is to import the css into the react component which allows for the css to be coupled to the component and for it to be specific to that component.

When you create a react application using create react app then it automatically creates a webpack configuration as well. It automatically adds hot reloading for a served development page.

07 - Passing Dynamic Data with Props

this can refer to variables in the react dev tools such as $r. $0, $1, etc refer to the most recent things that you clicked on in the react dev tools.

08 - Stateless Functional Components

For components that only have a render method that returns HTML, there’s no need for it to be a full blown react component. In that situation you can use a stateless functional component instead. Supposedly they’re more performant and saves a bit of code.

You can use a function to define stateless functional components:

function Header(props) {
	return (
		{/* html */}
	);
}

// or arrow function syntax: 
const Header = (props) => {
	return (
		{/* html */}
	);
}

// or arrow function syntax with an implicit return:
const Header = (props) => (
		{/* html */}
	);
// or arrow function syntax with an implicit return and destructured props:
// tagline is pulled from props.tagline
const Header = ({tagline, age}) => (
		{/* html */}
	);

09 - Routing with React Router

Routing is not baked into react. Two most popular are React Router and Next.js. The router in react is a component.

The <Route> tag works by sequentially going through the routes to see if it finds a match. <Route exact path="/" component={StorePicker}> matches, exactly, the / route.

<Route path="/stores/:storeId" component={App}> matches /stores/* and passes storeId to the App component at props.match.params.storeId.

Route provides the route params to the component that is mentioned in the route

10 - Helper and Utility Functions

Named exports are exports in ES6 modules that are not default exports. In React, if you put a value on an input element then React will error out since doing so creates a read-only field.

11 - Events, Refs, and this Binding

Events in React are very similar to those say in jQuery, AngularJS and other FE frameworks. React wraps events with its own synthetic events class. For example, you can use the onClick prop to pass a method for React when the element is clicked: <button onClick={this.handleClick}>Click Me!</button>. Notice that the handleClick function is not immediately invoked. It is invoked on every click so you must pass it a reference to the function and not a resolved function result.

Similarly, there’s an onSubmit event that you can use on forms.

Every event function is passed an event object: handleClick(event) {}

A golden rule in React is never to touch the DOM. However, there are some instances when you should but you should always default to not doing so.

To get the submitted form values you can use ref on the input. A deprecated way is to use ref with a string value.

<input
	type="text"
	required
	ref="myInput"
	placeholder="Store Name"
	defaultValue={getFunName()}
/>

Another way is to use a function ref (though a bit difficult to understand and grok):

<input
	type="text"
	required
	ref={(myInput) => this.myInput = myInput}
	placeholder="Store Name"
	defaultValue={getFunName()}
/>

The standard way is like the below now. You use this.myInput to reference the input ref that you created at the top of the class.

class MyClass extends React.Component {
	myInput = React.createRef();

	render() {
		<form>
			<input
				type="text"
				required
				ref={this.myInput}
				placeholder="Store Name"
				defaultValue={getFunName()}
			/>
		</form>
	}
}

Component Lifecycle Events

Binding

Any methods that are added to a component, beyond what is provided in React.Component, will not be bound to this in the context of the component. The solution is to bind your own methods as well. Add a constructor() with a call to super(). Inside of there, you call each of your methods with this.goToStore.bind(this).

Another solution is to use arrow functions which will be set to the instance instead of nothing:

class MyClass extends React.Component {
	goToStore = (event) => {
		// this is set to MyClass instance
	}
}

12 - Handling Events

When a component is a child of the router (meaning it is used by the router), then it is provided the routes that it needs to manage and manipulate routes.

Change the route by:

this.props.history.push(`/store/${storeName}`);

13 - Understanding State

State is an object that lives inside of a component and stores data for that component and possibly some of its children. It is a single source of truth. If you update the state, the data, then it will react and update all of the different places that rely upon it.

Every component in React can have its own state. However, it is often that you have a parent state on a higher component and you pass that down to the children. You can’t pass data up but you can pass it down.

You can initialize state in the constructor:

class App extends React.Component {
	constructor() {
		super();
		this.state = {};
	}
}

Or you can use a property which is the preferred way:

class App extends React.Component {
	state = {
		fishes: {},
		order: {}
	};
}

You can pass state using props:

class App extends React.Component {
	state = {
		fishes: {},
		order: {}
	};

	addFish = fish => {
		console.log("Adding a fish");
	}

	render() {
		return (
			<Inventory addFish={this.addFish} />
		);
	}
}

In order to update state in React you need to:

  1. take a copy of the existing staten: const fishes = {...this.state.fishes}

    • You don’t want to directly modify state since that is mutating the object and it causes issues with performance and with things updating out of order.
    • Use an object spread: .... A deep clone is not necessary.
  2. add the new object to the copy

  3. set the new composite object to state

    • Use this.setState(fishes)
    • This will trigger a change on the page
    • You don’t need to pass the entire state but just the individual elements

14 - Loading data into state onClick

Create state event functions in the same component in which the state lives.

15 - Displaying State with JSX

How to loop over fishes in JSX? JSX doesn’t have logic. Use JS for logic.

When creating an array of tracked items in React you need to pass a unique key property: <li key={myRandomVariable}></li>. Needs to be a string index (I believe).

16 - Updating Our State

You can disable a button by using the disabled prop: <button disabled={!isAvailable} >Add to Cart</button>

React does not give you access to the key when working with iterated items so you must use a different prop, in this case index, in order to get access to the key.

<Fish key={key} index={key} details={this.state.fishes[key]} addToOrder={this.addToOrder} />

17 - Displaying Order State with JSX

To add all of the properties from a state to the component, you can use an object spread. In the below example, the Order component would receive the props fishes and order. Doing this is not recommended since it is a best practice to explicitly define state when adding state to components.

state = {
	fishes: {},
	order: {}
};

<Order {...this.state}/>

When you’re doing too much inside of your render component, that typically means that you should create a new component and delegate some of the functionality to it.

Another pattern is that you can add separate render functions to a component.

18 - Persisting our State with Firebase

Use lifecycle methods to add in things such as persistence.

componentDidMount() {
	console.log('here');
}

We’ll be using this.ref though those refs are different than what we’ve been using in react. It refers to the firebase refs.

When you switch from one “store” to another, you can create memory leaks with Firebase since the browser is still listening for changes. You need to use the componentWillUnmount() method.

componentWillUnmount() {
	base.removeBinding(this.ref);
}

19 - Persisting Order State with LocalStorage

Use localStorage setItem() and getItem().

20 - Bi-directional Data Flow and Live State Editing

React doesn’t like it when you put state into an editable area without a plan to update it. Don’t use input value without adding an onChange() event.

Computed property names (new in ES6):

handleChange = event => {
	const updatedFish = {
		...this.props.fish,
		[event.currentTarget.name]: event.currentTarget.value
	};

	this.props.updateFish(this.props.index, updatedFish);
};

21 - Removing Items from State

To delete an item you first copy the state element and then you set the individual item to null and then you set the state again using the react function. You can’t use the delete keyword because of Firebase. It expects that you’ll set the item to null. It’s pretty much the same as the update except that you set it to null instead of the updated item.

deleteFish = (key) => {
	const fishes = { ...this.state.fishes };
	fishes[key] = null;
	this.setState({ fishes });
};

22 - Animating React Components

To add React transitions to a ul element, use the TransitionGroup component with a prop of component equal to ul:

<TransitionGroup component="ul" className="order">
	{ orderIds.map(this.renderOrder) }
</TransitionGroup>

To add in a React CSS transition to an li, use the CSSTransition component providing a classNames, timeout, and key prop:

<CSSTransition classNames="order" key={key} timeout={{enter: 250, exit: 250}}>
	<li>
		// your code
	</li>
</CSSTransition>

Setting max-height to 0 allows you to transition from a fixed height to a calculated height. It’s a bit of a hack.

23 - Component Validation with PropTypes

The PropTypes package used to be built in to React and now it is an alternative to Flow or Typescript. Here’s an example for a stateless functional component:

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

const Header = props => {
    render() {
        return (
            <header className="top">
                <h3 className="tagline">
                    <span>{props.tagline}</span>
                </h3>
            </header>
        );
    }
}

Header.propTypes = {
  tagline: PropTypes.string.isRequired,
};

export default Header;

For a regular react component, you can use a static property:

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

class AddFishForm extends React.Component {
  static propTypes = {
    addFish: PropTypes.func
  };
  render() {
    return (
      // redacted
    );
  }
}

export default AddFishForm;

29 - Eject from create-react-app

Use eject from create react app when you need to add in custom build processes or compilation steps. It is a permament change so do it on a separate branch first to test. It adds several dependencies and you can edit the webpack file directly.