React
Progression
ID | Status | Learning Item | Type | Related Project | Date Completed |
---|---|---|---|---|---|
1 | Intro to React | Project | Tic-Tac-React | 5/10/2020 | |
2 | Intro to React - Extra Credit (4/6) | Project | Tic-Tac-React | ||
3 | Start Learning React | Course | |||
4 | Not Started | Learn React | Course | ||
5 | Not Started | Getting Started with React (2019 Edition) | Course | ||
6 | Demystifying React Hooks | Blog | |||
7 | React Hooks - My Introduction | Blog | |||
8 | Learn React Hooks In One Hour | Course | |||
9 | 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
- Only call Hooks at the top level
- 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!