Visit the main site

Category:

  • Javascript library

Progression

ID Status Learning Item Type Related Project Date Completed
1 Complete Intro to React Project Tic-Tac-React 5/10/2020
2 In progress Intro to React - Extra Credit (4/6) Project Tic-Tac-React
3 Up next Start Learning React Course
4 Not Started Learn React Course
5 Not Started Getting Started with React (2019 Edition) Course
6 Up next Demystifying React Hooks Blog
7 Complete React Hooks - My Introduction Blog
8 Complete Learn React Hooks In One Hour Course
9 Complete Understanding Functional Components vs. Class Components in React Blog
Not Started Leveling up with React - React Router Tutorial
Not Started Leveling Up With React - Container Components Tutorial
Not Started Leveling Up with React - Redux Tutorial
Not Started Build a movie search app in React Project
Not Started Build an app with React and GraphQL Project
Not Started Environments in Create React App Blog
Not Started Setting up a React Toolchain Official Docs
Not Started Get Really Good at React Course
Not Started React 2025 Course

Components

Components are the building blocks created with React, and can be used to split a UI up into independent reusable pieces.

Components consume props, which are arbitrary inputs, which can be used to manipulate how the component displays.

Values, passed as props, can be delivered to user-declared components as such:

const element = <Welcome name="World" />;

Props should be named from the component’s own point of view, rather than the context in which it is being used.

Components can be built up of further components, allowing for high abstraction and flexibility. The general recommendation is that if a component is used several times or is complex, it is a good candidate to be extracted to a separate component.

Class Components

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

Class components tend to be more verbose and typically require a minimum of constructor() and render() lifecycle methods, and may take advantage of other lifecycle methods such as componentDidMount() (runs after component rendered to DOM) and componentWillUnmount() (runs as the component is being removed from the DOM).

Class components are known as “stateful,” as they can implement logic and state.

Function Components

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

Function components utilize hooks to simplify accessing lifecycle methods. They are also considered “stateless,” as they simply accept data and display it in some form.

Or at least, this used to be the case. With the introduction of React Hooks in React 16.8, function components have rapidly gained functionality, and are the current preference for component construction.

Functional components can also be declared in the export to keep things exceptionally tidy:

import React, { useState } from 'react'

export default function MyComponent() {
  
}

{ useState } within our import is destructured from React so that it can be used directly instead of React.useState().

Props

Props passed to a component are read-only, and should not be modified. The rule is:

All React components must act like pure functions with respect to their props.

As an example:

function sum(a, b) {
  return a + b;
}

…is a pure function, as it always returns the exact same results for the same inputs.

State

State is similar to props, but is private and fully controlled by a component.

While <Clock date={new Date()} /> would make the date object accessible within Clock via {this.props.date.toLocaleTimeString()}, using state allows the date object to be fully self-contained within Clock:

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

  render() {
    return (
      <div>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

While state can be initialized with this.state, it can also be updated with this.setState(). this.state can ONLY be assigned in the constructor.

Directly modifying the state value will not re-render a component! Using setState() will trigger the re-render correctly.

Updates to both props and state may be batched asynchronously by React for performance, and may be unreliable to use to set state.

UNRELIABLE:

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

To counteract this, setState() can alternatively accept a function that uses the previous state and the updated props to update state once properly processed.

CORRECT:

this.setState(function(state, props) {
  return {
    counter: state.counter + props.increment
  };
});

…which can be simplified further with arrow notation:

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

The state can contain multiple independent variables, and each can be updated independent of each other with separate setState() calls. The merge is shallow, so only the state variables passed to setState() are replaced.

Hooks

Hooks are powerful functions that expose class-like capabilities to functional components. The most commonly used hooks are useState() and useEffect(), and many have direct similarities to class lifecycle methods.

Rules

  1. Only call Hooks at the top level
  2. Only call Hooks from React functions

Conversion Reference

Classes Hooks
constructor(props) {
super(props)
this.state = { varName: initValue }
}
const [varName, setVarName] = useState(initValue);
componentDidMount() {
console.log(“Hello”);
}
React.useEffect(() => {
console.log(“Hello”);
}, []);
componentWillUnmount() {
console.log(“Bye”);
}
React.useEffect(() => {
return () => {
console.log(“Bye”);
}
}, []);

useState

In a class component, state is declared like this:

constructor(props) {
  super(props)
  this.state = { date: new Date() }
}

Without implementing the constructor and calling super(props), all the state variables that you are trying to use will be undefined, per the React documentation.

In a function component, however, the useState() hook allows you to set both a variable (left) and a function to update the value of said variable (right):

const [time, setTime] = useState(new Date());

useState is imported from React, and can either be accessed by React.useState() or deconstructed at import with { useState } and accessed directly.

Let’s look at an iterative example. Given a state variable count:

const [count, setCount] = useState(0)

…let’s say we have a button to increase the value of count by one per click:

<button onClick={setCount(count + 1)}>+</button>

Declaring the function this way executes immediately, however, so we want to use an in-line function to have it execute on the click:

<button onClick={() => setCount(count + 1)}>+</button>

State updates are processed asynchronously, so by using the state variable directly, we are relying on the current state of count and run the risk of calling setCount with an outdated value. We can counteract that by passing a function, with the current state as it’s argument, to setCount rather than a value directly:

<button onClick={() => setCount(currentCount => currentCount + 1)}>-</button>

To elaborate on this, let’s take the following example:

setCount(count + 1) is effectively the equivalent of this.setState({ count: this.state.count + 1 })

When it is critical that a state gets the updated value, we would change

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

to

this.setState((state) => ({ coins: state.coins + 1 }));

Therefore, the hooks equivalent would be:

setCount(oldCount => oldCount + 1)


For related state variables, it may be helpful to declare them in groupings using destructured arrays:

const [[windowWidth, windowHeight], setWindowSize] = useState([window.innerWidth, window.innerHeight])

setWindowSize([window.innerWidth, window.innerHeight])

useEffect

The useEffect hook takes the following parameters:

  • A function to execute on trigger
  • An array of things that should trigger the effect when updated
    • An empty array will only run on the first render
    • A missing second parameter will create an infinite loop
useEffect(getValue, [])

When a target prop is updated, the useEffect’s function will run.

useRef

We can utilize useRef to get a reference to a DOM element.

Add ref={} within the target element properties. The reference variable itself is an option with value .current that is set to the current value.

const inputRef = useRef()

useEffect(() => {
  inputRef.current.focus()
}, [])

<input ref={inputRef} ...>

In another example, we can use the reference to get useful information:

const headerRef = useRef({ offsetHeight: 0 })

<header ref={headerRef}>

<Canvas
  color={activeColor}
  height={window.innerHeight - headerRef.current.offsetHeight}
/>

useCallback

useCallback utilizes the exact same pattern as useEffect - a function (or functional component in our case), and an array of conditions:

export default function Playground() {
  const [count, setCount] = useState(30)
  {...}
  const cb = useCallback(num => console.log(num), [count])

  return (
    <div>
      {...}
      <Calculate cb={cb} num={count} />
    </div>
  )
}

function Calculate({ cb, num }) {
  cb(num)
  const renderCount = useRef(1)
  return <div>{renderCount.current++}</div>
}

In the example above, the num will be updated in the console every time that count is updated.

React.memo

Going back to useCallback, we can use React.memo to check and see if the inputs are the same, and not render if they are:

const Calculate = React.memo(({ cb, num }) => {
  cb(num)
  const renderCount = useRef(1)
  return <div>{renderCount.current++}</div>
})

useMemo

useMemo can be utilized to cache the results of long equations for reuse.

useCallback(() => console.log('useCallback')) // return the function
useMemo(() => console.log('useMemo')) // return the result of the function

Custom Hooks

Names for custom hooks should start with use, similar to useState and the other native react hooks. Formatting of the actual functions are similar, as well.

Try to make them as small, composable, and reusable as possible!

Resources