React Hooks: Cheat Sheets
React Hooks: Cheat Sheets
CHEAT SHEETS
SUMMARY
#1 Manage state
useState
const [count, setCount] = useState(initialCount);
useReducer
const [state, dispatch] = useReducer(
reducer,
initialState,
initialDispatch
);
useLayoutEffect
useLayoutEffect(() => {
applyBlockingEffect(dependencies);
return cleanupEffect();
}, [dependencies]);
useCallback
const memoizedCallback = useCallback(
expensiveFn(dependencies),
[dependencies]
);
#5 Use refs
useRef
const ref = useRef();
useImperativeHandle
useImperativeHandle(
ref,
createHandle,
[dependencies]
)
#6 Reusability
Extract reusable behaviour into custom hooks
#1 Manage state
useState
Class way
const initialCount = 0;
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button
onClick={() => this.setState(({count}) => ({ count: count + 1 }))}
>
Click me
</button>
</div>
);
}
}
Hook way
const initialCount = 0;
function Counter() {
const [count, setCount] = useState(initialCount);
return (
<div>
<p>You clicked {count} times</p>
<button
onClick={() => setCount((c) => c + 1)}
>
Click me
</button>
</div>
);
}
#1 Manage state
useReducer
function Counter({initialState}) {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>+</button>
</>
);
}
#2 Handle side effects
useEffect
Class way
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentDidUpdate(prevProps) {
if(this.props.friend.id !== prevProps.id) {
ChatAPI.unsubscribeFromFriendStatus(
prevProps.friend.id,
this.handleStatusChange
);
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
}
render() {
if (this.state.isOnline === null) {
return 'Loading...';
}
return this.state.isOnline ? 'Online' : 'Offline';
}
}
#2 Handle side effects
Hooks way
Class way
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null); // Store friend's online
status
useEffect(
() => {
// Update status when the listener triggers
ChatAPI.subscribeToFriendStatus(
props.friend.id,
handleStatusChange
);
Almost the same as useEffect, but fires synchronously after the render phase. Use
this to safely read from or write to the DOM
function ColoredComponent({color}) {
const ref = useRef();
useLayoutEffect(() => {
const refColor = ref.current.style.color;
console.log(`${refColor} will always be the same as ${color}`);
ref.current.style.color = "rgba(255,0,0)";
}, [color]);
useEffect(() => {
const refColor = ref.current.style.color;
console.log(
`but ${refColor} can be different from ${color} if you play with the
DOM`
);
}, [color]);
return (
<div ref={ref} style={{ color: color }}>
Hello React hooks !
</div>
);
}
Hook way
import { useContext } from 'react';
function Header() {
return ...
}
// Wrong
const value = useContext(Context.Consumer);
// Right
const value = useContext(Context);
#4 Memoize everything
useMemo
function RandomColoredLetter(props) {
const [color, setColor] = useState('#fff')
const [letter, setLetter] = useState('a')
const handleColorChange = useMemo(() => () => setColor(randomColor()), []);
const handleLetterChange = useMemo(() => () => setLetter(randomColor()), []);
return (
<div>
<ColorPicker handleChange={handleColorChange} color={color} />
<LetterPicker handleChange={handleLetterChange} letter={letter} />
<hr />
<h1 style={{color}}>{letter}</h1>
</div>
)
}
useCallback
function RandomColoredLetter(props) {
const [color, setColor] = useState('#fff')
const [letter, setLetter] = useState('a')
const handleColorChange = useCallback(() => setColor(randomColor()), []);
const handleLetterChange = useCallback(() => setLetter(randomColor()), []);
return (
<div>
<ColorPicker handleChange={handleColorChange} color={color} />
<LetterPicker handleChange={handleLetterChange} letter={letter} />
<hr />
<h1 style={{color}}>{letter}</h1>
</div>
)
}
#5 Use refs
useRef
useRef can just be used as a common React ref :
function TextInput() {
const inputRef = useRef(null);
const onBtnClick = () => inputRef.current.focus();
return (
<>
<input ref={ref} />
<button onClick={onBtnClick}>Focus the text input</button>
</>
)
}
But it also allows you to just hold a mutable value through any render. Also,
mutating the value of ref.current will not cause any render
#5 Use refs
useImperativeHandle
Allows you to customize the exposed interface of a component when using a ref.
The following component will automatically focus the child input when mounted :
useImperativeHandle(ref, () => ({
focusInput: () => inputRef.current.focus();
});
return (
<Fragment>
<input ref={inputRef} />
<button onClick={onBtnClick}>Focus the text input</button>
</Fragment>
)
}
function Parent() {
const ref = useRef(null);
useEffect(() => {
ref.focusInput();
}, []);
return (
<div>
<TextInputWithRef ref={ref} />
</div>
);
}
#6 Reusability
Extract reusable behaviour into custom hooks
return () => {
node.removeEventListener("mouseover", handleMouseOver);
node.removeEventListener("mouseout", handleMouseOut);
};
}
}, [ref.current]);
// return the pair of the exposed ref and it's hovered state
return [ref, value];
}
Core
:
return (
<p>Logged out.</p>
Stateless Components <h1>
}
It is {this.state.date.toLocaleTimeString()}.
</div>
// Stateless React Component </h1>
)
const Headline = () => { )
}
return <h1>React Cheat Sheet</h1> }
// CodePen Demo: http://bit.ly/react-if-statements
}
componentWillMount() {
// Component that receives props // fires immediately before the initial render Tools and Resources
const Greetings = (props) => { }
return <p>You will love it {props.name}.</p> componentDidMount() { // Create React App
} // fires immediately after the initial render http://bit.ly/react-app
} // React Dev Tools for Chrome
// Component must only return ONE element (eg. DIV) componentWillReceiveProps() { http://bit.ly/react-dev-tools
const Intro = () => { // fires when component is receiving new props // React Code Snippets for Visual Studio Code
return ( } http://bit.ly/react-es6-snippets-for-vscode
<div> shouldComponentUpdate() { // Babel for Sublime Text 3
Lifecycle Methods
<Headline /> // fires before rendering with new props or state http://bit.ly/babel-sublime
<p>Welcome to the React world!</p> }
<Greetings name=”Petr” /> componentWillUpdate() {
</div> // fires immediately before rendering Free Online Course
}
)
}
// with new props or state
React.js 101
componentDidUpdate() { The Quickest Way To Get
ReactDOM.render( // fires immediately after rendering with new P or S
<Intro />, }
Started With React.js
document.getElementById('root') componentWillUnmount() {
); // fires immediately before component is unmounted http://bit.ly/get-react-101
// from DOM (removed)
// Components and Props API - http://bit.ly/react-props }
// CodePen Demo: http://bit.ly/react-simple } Coming Soon!
// CodePen Demo: http://bit.ly/react-es6-class
/*
JSX allows us to write in a syntax almost identical to plain HTML.
As a result, JSX is a powerful tool to structure our applications.
JSX uses all valid HTML tags (i.e. div/span, h1-h6, form/input, img, etc).
*/
<div>Hello React!</div>
/*
Note: this JSX would not be visible because it is needs to be rendered by our application using ReactDOM.render()
*/
JSX is the most common way to structure React applications, but JSX is not required for React
JSX is not understood by the browser. JSX needs to be compiled to plain JavaScript, which the browser can
understand.
/*
When our project is built to run in the browser, our JSX will be converted by Babel into simple React.createElement() function calls
From this...
*/
const greeting = <div>Hello React!</div>;
/* ...into this: */
"use strict";
/*
We can write JSX like plain HTML, but it's actually made using JavaScript functions.
Because JSX is JavaScript, not HTML, there are some differences:
1) Some JSX attributes are named differently than HTML attributes. Why? Because some attribute words are reserved words in JavaScrip
Also, because JSX is JavaScript, attributes that consist of multiple words are written in camelcase:
*/
<div id="header">
<h1 className="title">Hello React!</h1>
</div>
/*
2) JSX elements that consist of only a single tag (i.e. input, img, br elements) must be closed with a trailing forward slash to be
*/
/*
3) JSX elements that consists of an opening and closing tag (i.e. div, span, button element), must have both or be closed with a tra
*/
Inline styles can be added to JSX elements using the style attribute
Styles are updated within an object, not a set of double quotes, as with HTML
/*
Properties that accept pixel values (like width, height, padding, margin, etc), can use integers instead of strings.
For example: fontSize: 22. Instead of: fontSize: "22px"
*/
<h1 style={{ color: "blue", fontSize: 22, padding: "0.5em 1em" }}>
Hello React!
</h1>;
JSX gives us the full power of JavaScript directly within our user interface
/*
JSX elements are expressions (resolve to a value) and therefore can be assigned to plain JavaScript variables...
*/
const greeting = <div>Hello React!</div>;
JSX allows us to insert (or embed) simple JavaScript expressions using the curly braces syntax
/* We can insert primitive JS values (i.e. strings, numbers, booleans) in curly braces: {} */
const greeting = <div>Hello React in {year}</div>;
/* Note: trying to insert object values (i.e. objects, arrays, maps) in curly braces will result in an error */
JSX allows us to nest elements within one another, like we would HTML
/*
To write JSX on multiple lines, wrap in parentheses: ()
JSX expressions that span multiple lines are called multiline expressions
*/
const greeting = (
Comments in JSX are written as multiline JavaScript comments, written between curly braces
const greeting = (
<div>
{/* This is a single line comment */}
<h1>Hello!</h1>
<p>Welcome to React</p>
{/* This is a
multiline
comment */}
</div>
);
ReactDOM.render() : used to render (show) our app by mounting it onto an HTML element
A JSX element: called a "root node", because it is the root of our application. Meaning, rendering it will render all
children within it
An HTML DOM element: Where the app is inserted within an HTML page. The element is usually a div with an id of
"root", located in an index.html file
// Packages can be installed locally or brought in through a CDN link (added to head of HTML document)
import React from "react";
import ReactDOM from "react-dom";
There are two types of components in React: function components and class components
Component names, for function or class components, are capitalized to distinguish them from plain JavaScript
functions that do not return JSX
/*
Function component
Note the capitalized function name: 'Header', not 'header'
*/
function Header() {
return <h1>Hello React</h1>;
}
// Function components which use an arrow function syntax are also valid
const Header = () => <h1>Hello React</h1>;
/*
Class component
Class components have more boilerplate (note the 'extends' keyword and 'render' method)
*/
class Header extends React.Component {
Components, despite being functions, are not called like ordinary JavaScript functions
Components are executed by rendering them like we would JSX in our app
The huge benefit of components is their ability to be reused across our apps, wherever we need them
Since components leverage the power of JavaScript functions, we can logically pass data to them, like we would by
passing it one or more arguments
/*
The Header and Footer components can be reused in any page in our app.
Components remove the need to rewrite the same JSX multiple times.
*/
Props look identical to attributes on plain JSX/HTML elements, but you can access their values within the component
itself
Props are available in parameters of the component to which they are passed. Props are always included as properties
of an object
/*
What if we want to pass custom data to our component from a parent component?
For example, to display the user's name in our app header.
*/
/*
To do so, we add custom 'attributes' to our component called props
We can add many of them as we like and we give them names that suit the data we pass in.
To pass the user's name to the header, we use a prop we appropriately called 'username'
Another way to say this is that props should never be mutated, since props are a plain JavaScript object
/*
Components should operate as 'pure' functions.
That is, for every input, we should be able to expect the same output.
This means we cannot mutate the props object, only read from it.
*/
The children prop is useful if we want to pass elements / components as props to other components
function Layout(props) {
return <div className="container">{props.children}</div>;
}
// The children prop is very useful for when you want the same
// component (such as a Layout component) to wrap all other components:
function IndexPage() {
return (
<Layout>
<Header />
<Hero />
<Footer />
</Layout>
);
}
// different page, but uses same Layout component (thanks to children prop)
function AboutPage() {
return (
<Layout>
<About />
<Footer />
</Layout>
);
}
Again, since components are JavaScript expressions, we can use them in combination with if-else statements and
switch statements to conditionally show content
function Header() {
const isAuthenticated = checkAuth();
To use conditions within a component's returned JSX, you can use the ternary operator or short-circuiting && and ||
operators)
function Header() {
const isAuthenticated = checkAuth();
return (
<nav>
{/* if isAuth is true, show nothing. If false, show Logo */}
{isAuthenticated || <Logo />}
{/* if isAuth is true, show AuthenticatedApp. If false, show Login */}
{isAuthenticated ? <AuthenticatedApp /> : <LoginScreen />}
{/* if isAuth is true, show Footer. If false, show nothing */}
{isAuthenticated && <Footer />}
</nav>
);
}
Fragments are special components for displaying multiple components without adding an extra element to the DOM
Fragments are ideal for conditional logic that have multiple adjacent components or elements
/*
We can improve the logic in the previous example.
If isAuthenticated is true, how do we display both the AuthenticatedApp and Footer components?
*/
function Header() {
const isAuthenticated = checkAuth();
return (
<nav>
<Logo />
{/*
We can render both components with a fragment.
Fragments are very concise: <> </>
*/}
{isAuthenticated ? (
<>
<AuthenticatedApp />
<Footer />
</>
) : (
<Login />
)}
</nav>
);
}
/*
Note: An alternate syntax for fragments is React.Fragment:
<React.Fragment>
<AuthenticatedApp />
<Footer />
</React.Fragment>
*/
function App() {
const people = ["John", "Bob", "Fred"];
// can interpolate returned list of elements in {}
return (
<ul>
{/* we're passing each array element as props to Person */}
{people.map((person) => (
<Person name={person} />
))}
</ul>
);
}
Each React element within a list of elements needs a special key prop
Keys are essential for React to be able to keep track of each element that is being iterated over with the .map()
function
React uses keys to performantly update individual elements when their data changes (instead of re-rendering the
entire list)
Keys need to have unique values to be able to identify each of them according to their key value
function App() {
const people = [
{ id: "Ksy7py", name: "John" },
{ id: "6eAdl9", name: "Bob" },
{ id: "6eAdl9", name: "Fred" },
];
return (
<ul>
{/* keys need to be primitive values, ideally a unique string, such as an id */}
{people.map((person) => (
<Person key={person.id} name={person.name} />
))}
</ul>
);
}
/* If you don't have some identifier with your set of data that is a unique
and primitive value, use the second parameter of .map() to get each elements index */
function App() {
const people = ["John", "Bob", "Fred"];
return (
<ul>
{/* use array element index for key */}
{people.map((person, i) => (
<Person key={i} name={person} />
))}
</ul>
);
}
You cannot listen for events on React components; only on JSX elements. Adding a prop called onClick , for example,
to a React component would just be another property added to the props object
/* In HTML, onclick is all lowercase, plus the event handler includes a set of parentheses after being referenced */
<button onclick="handleToggleTheme()">
Toggle Theme
</button>
/*
In JSX, onClick is camelcase, like attributes / props.
We also pass a reference to the function with curly braces.
*/
<button onClick={handleToggleTheme}>
Toggle Theme
</button>;
The most essential React events to know are onClick , onChange , and onSubmit
onChange handles keyboard events (namely a user typing into an input or textarea)
function App() {
function handleInputChange(event) {
/* When passing the function to an event handler, like onChange we get access to data about the event (an object) */
const inputText = event.target.value; // text typed into the input
const inputName = event.target.name; // 'email' from name attribute
}
function handleClick(event) {
/* onClick doesn't usually need event data, but it receives event data as well that we can use */
console.log("clicked!");
const eventType = event.type; // "click"
const eventTarget = event.target; // <button>Submit</button>
}
function handleSubmit(event) {
/*
When we hit the return button, the form will be submitted, as well as when a button with type="submit" is clicked.
We call event.preventDefault() to prevent the default form behavior from taking place, which is to send an HTTP request and reloa
*/
event.preventDefault();
const formElements = event.target.elements; // access all element within form
const inputValue = event.target.elements.emailAddress.value; // access the value of the input element with the id "emailAddress"
}
return (
<form onSubmit={handleSubmit}>
<input
id="emailAddress"
type="email"
name="email"
onChange={handleInputChange}
/>
<button onClick={handleClick}>Submit</button>
</form>
);
}
State allows us to access and update certain values in our components over time
When we call useState we can give our state a default value by providing it as the first argument when we call useState
/*
How do you create a state variable?
Syntax: const [stateVariable] = React.useState(defaultValue);
*/
function App() {
const [language] = React.useState("JavaScript");
/*
We use array destructuring to declare state variable.
Like any variable, we declare we can name it what we like (in this case, 'language').
*/
Note: Any hook in this section is from the React core library and can be imported individually
function App() {
const [language] = useState("javascript");
useState also gives us a 'setter' function to update the state after it is created
function App() {
/*
The setter function is always the second destructured value.
The naming convention for the setter function is to be prefixed with 'set'.
*/
const [language, setLanguage] = React.useState("javascript");
return (
<div>
<button onClick={() => setLanguage("python")}>Learn Python</button>
{/*
Why use an inline arrow function here instead of immediately calling it like so: onClick={setterFn()}?
If so, setLanguage would be called immediately and not when the button was clicked by the user.
*/}
<p>I am now learning {language}</p>
</div>
);
}
/*
Note: whenever the setter function is called, the state updates,
and the App component re-renders to display the new state.
Whenever state is updated, the component will be re-rendered
*/
function App() {
const [language, setLanguage] = React.useState("python");
const [yearsExperience, setYearsExperience] = React.useState(0);
return (
<div>
<button onClick={() => setLanguage("javascript")}>
Change language to JS
If the new state depends on the previous state, to guarantee the update is done reliably, we can use a function within
the setter function that gives us the correct previous state
/* We have the option to organize state using whatever is the most appropriate data type, according to the data we're managing */
function App() {
const [developer, setDeveloper] = React.useState({
language: "",
yearsExperience: 0,
});
function handleChangeYearsExperience(event) {
const years = event.target.value;
/* We must pass in the previous state object we had with the spread operator to spread it all of its properties */
setDeveloper({ ...developer, yearsExperience: years });
}
return (
<div>
{/* No need to get previous state here; we are replacing the entire object */}
<button
onClick={() =>
setDeveloper({
language: "javascript",
yearsExperience: 0,
})
}
>
Change language to JS
</button>
{/* We can also pass a reference to the function */}
<input
type="number"
value={developer.yearsExperience}
onChange={handleChangeYearsExperience}
/>
<p>I am now learning {developer.language}</p>
<p>I have {developer.yearsExperience} years of experience</p>
</div>
);
}
If you are managing multiple primitive values, using useState multiple times is often better than using useState once
with an object. You don't have to worry about forgetting to combine the old state with the new state
function App() {
const [developer, setDeveloper] = React.useState({
language: "",
yearsExperience: 0,
isEmployed: false,
});
function handleToggleEmployment(event) {
/* We get the previous state variable's value in the parameters.
We can name 'prevState' however we like.
*/
setDeveloper((prevState) => {
return { ...prevState, isEmployed: !prevState.isEmployed };
// It is essential to return the new state from this function
});
}
return (
Side effects are where we need to reach into the outside world. For example, fetching data from an API or working
with the DOM
Side effects are actions that can change our component state in an unpredictable fashion (that have cause 'side
effects')
useEffect accepts a callback function (called the 'effect' function), which will by default run every time there is a re-
render
useEffect runs once our component mounts, which is the right time to perform a side effect in the component lifecycle
/* What does our code do? Picks a color from the colors array and makes it the background color */
import React, { useState, useEffect } from "react";
function App() {
const [colorIndex, setColorIndex] = useState(0);
const colors = ["blue", "green", "red", "orange"];
/*
We are performing a 'side effect' since we are working with an API.
We are working with the DOM, a browser API outside of React.
*/
useEffect(() => {
document.body.style.backgroundColor = colors[colorIndex];
});
/* Whenever state is updated, App re-renders and useEffect runs */
function handleChangeColor() {
/* This code may look complex, but all it does is go to the next color in the 'colors' array, and if it is on the last color, go
const nextIndex = colorIndex + 1 === colors.length ? 0 : colorIndex + 1;
setColorIndex(nextIndex);
}
To avoid executing the effect callback after each render, we provide a second argument, an empty array
function App() {
/*
With an empty array, our button doesn't work no matter how many times we click it...
The background color is only set once, when the component first mounts.
*/
useEffect(() => {
document.body.style.backgroundColor = colors[colorIndex];
}, []);
/*
How do we not have the effect function run for every state update but still have it work whenever the button is clicked?
*/
The dependencies array is the second argument and if any one of the values in the array changes, the effect function
runs again
function App() {
const [colorIndex, setColorIndex] = React.useState(0);
/*
Let's add colorIndex to our dependencies array
When colorIndex changes, useEffect will execute the effect function again
*/
useEffect(() => {
document.body.style.backgroundColor = colors[colorIndex];
/*
When we use useEffect, we must think about what state values
we want our side effect to sync with
*/
}, [colorIndex]);
function handleChangeIndex() {
const next = colorIndex + 1 === colors.length ? 0 : colorIndex + 1;
setColorIndex(next);
}
useEffect lets us unsubscribe from certain effects by returning a function at the end
function MouseTracker() {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
React.useEffect(() => {
// .addEventListener() sets up an active listener...
window.addEventListener("mousemove", handleMouseMove);
function handleMouseMove(event) {
setMousePosition({
x: event.pageX,
y: event.pageY,
});
}
return (
<div>
<h1>The current mouse position is:</h1>
<p>
X: {mousePosition.x}, Y: {mousePosition.y}
</p>
</div>
);
}
useEffect is the hook to use when you want to make an HTTP request (namely, a GET request when the component
mounts)
Note that handling promises with the more concise async/await syntax requires creating a separate function Why?
The effect callback function cannot be async)
React.useEffect(() => {
fetch(endpoint)
.then((response) => response.json())
We call useRef (at top of component) and attach the returned value to the element's ref attribute to refer to it
Once we create a reference, we use the current property to modify (mutate) the element's properties or can call any
available methods on that element (like .focus() to focus an input)
function App() {
const [query, setQuery] = React.useState("react hooks");
/* We can pass useRef a default value.
We don't need it here, so we pass in null to reference an empty object
*/
const searchInput = useRef(null);
function handleClearSearch() {
/*
.current references the input element upon mount
useRef can store basically any value in its .current property
*/
searchInput.current.value = "";
searchInput.current.focus();
}
return (
<form>
<input
type="text"
onChange={(event) => setQuery(event.target.value)}
ref={searchInput}
/>
<button type="submit">Search</button>
<button type="button" onClick={handleClearSearch}>
Clear
</button>
</form>
);
}
In particular, React.memo performs a process called memoization that helps us prevent our components from re-
rendering when they do not need to be (see React.useMemo for more complete definition of memoization)
/*
In the following application, we are keeping track of our programming skills. We can create new skills using an input, they are adde
*/
function App() {
const [skill, setSkill] = React.useState("");
const [skills, setSkills] = React.useState(["HTML", "CSS", "JavaScript"]);
function handleChangeInput(event) {
setSkill(event.target.value);
}
function handleAddSkill() {
setSkills(skills.concat(skill));
}
return (
<>
<input onChange={handleChangeInput} />
<button onClick={handleAddSkill}>Add Skill</button>
<SkillList skills={skills} />
</>
);
}
/* But the problem, if you run this code yourself, is that when we type into the input, because the parent component of SkillList (A
/* However, once we wrap the SkillList component in React.memo (which is a higher-order function, meaning it accepts a function as a
const SkillList = React.memo(({ skills }) => {
console.log("rerendering");
return (
<ul>
{skills.map((skill, i) => (
<li key={i}>{skill}</li>
))}
</ul>
);
});
Callback functions are the name of functions that are "called back" within a parent component.
The most common usage is to have a parent component with a state variable, but you want to update that state from
a child component. What do you do? You pass down a callback function to the child from the parent. That allows us to
update state in the parent component.
useCallback functions in a similar way as React.memo. It memoizes callback functions, so it is not recreated on every
re-render. Using useCallback correctly can improve the performance of our app
/* Let's keep the exact same App as above with React.memo, but add one small feature. Let's make it possible to delete a skill when
function App() {
const [skill, setSkill] = React.useState("");
const [skills, setSkills] = React.useState(["HTML", "CSS", "JavaScript"]);
function handleChangeInput(event) {
setSkill(event.target.value);
}
function handleAddSkill() {
setSkills(skills.concat(skill));
}
function handleRemoveSkill(skill) {
setSkills(skills.filter((s) => s !== skill));
/* Next, we pass handleRemoveSkill down as a prop, or since this is a function, as a callback function to be used within SkillList
return (
<>
<input onChange={handleChangeInput} />
<button onClick={handleAddSkill}>Add Skill</button>
<SkillList skills={skills} handleRemoveSkill={handleRemoveSkill} />
</>
);
}
/* When we try typing in the input again, we see rerendering in the console every time we type. Our memoization from React.memo is b
What is happening is the handleRemoveSkill callback function is being recreated everytime App is rerendered, causing all children
Try it yourself!
*/
const SkillList = React.memo(({ skills, handleRemoveSkill }) => {
console.log("rerendering");
return (
<ul>
{skills.map((skill) => (
<li key={skill} onClick={() => handleRemoveSkill(skill)}>
{skill}
</li>
))}
</ul>
);
});
useMemo allows us to memoize, or remember the result of expensive calculations when they have already been made
for certain inputs.
Memoization means that if a calculation has been done before with a given input, there's no need to do it again,
because we already have the stored result of that operation.
useMemo returns a value from the computation, which is then stored in a variable
/* Building upon our skills app, let's add a feature to search through our available skills through an additional search input. We c
*/
function App() {
const [skill, setSkill] = React.useState("");
const [skills, setSkills] = React.useState(["HTML", "CSS", "JavaScript"]);
function handleChangeInput(event) {
setSkill(event.target.value);
}
function handleAddSkill() {
setSkills(skills.concat(skill));
}
return (
// /* Let's imagine we have a list of thousands of skills that we want to search through. How do we performantly find and show the s
function SearchSkills() {
const [searchTerm, setSearchTerm] = React.useState("");
/* We use React.useMemo to memoize (remember) the returned value from our search operation and only run when it the searchTerm cha
const searchResults = React.useMemo(() => {
return skills.filter((s) => s.includes(searchTerm));
}, [searchTerm]);
function handleSearchInput(event) {
setSearchTerm(event.target.value);
}
return (
<>
<input onChange={handleSearchInput} />
<ul>
{searchResults.map((result, i) => (
<li key={i}>{result}</li>
))}
</ul>
</>
);
}
/*
React Context helps us avoid creating multiple duplicate props.
This pattern is also called props drilling.
*/
/* In this app, we want to pass the user data down to the Header component, but it first needs to go through a Main component which
function App() {
const [user] = React.useState({ name: "Fred" });
return (
// First 'user' prop
<Main user={user} />
);
}
Context is helpful for passing props down multiple levels of child components from a parent component
/*
Here is the previous example rewritten with Context.
First we create context, where we can pass in default values
function App() {
const [user] = React.useState({ name: "Fred" });
return (
/*
We wrap the parent component with the Provider property
We pass data down the component tree on the value prop
*/
<UserContext.Provider value={user}>
<Main />
</UserContext.Provider>
);
}
/*
We can remove the two 'user' props. Instead, we can just use the Consumer property to consume the data where we need it
*/
const Header = () => (
/* We use a pattern called render props to get access to the data */
<UserContext.Consumer>
{(user) => <header>Welcome, {user.name}!</header>}
</UserContext.Consumer>
);
The useContext hook can remove this unusual-looking render props pattern to consume context in any function
component that is a child of the Provider
function Header() {
/* We pass in the entire context object to consume it and we can remove the Consumer tags */
const user = React.useContext(UserContext);
switch (action.type) {
/* If action.type has the string 'LOGIN' on it, we get data from the payload object on action */
case "LOGIN":
return {
username: action.payload.username,
email: action.payload.email,
isAuth: true,
};
case "SIGNOUT":
return {
username: "",
isAuth: false,
};
default:
/* If no case matches the action received, return the previous state */
return state;
}
}
Reducers can be used in React with the useReducer hook in order to manage state across our app, as compared to
useState (which is for local component state)
useReducer can be paired with useContext to manage data and pass it around components easily
useReducer + useContext can be an entire state management system for our apps
function App() {
// useReducer requires a reducer function to use and an initialState
const [state, dispatch] = useReducer(reducer, initialState);
// we get the current result of the reducer on 'state'
function handleSignout() {
dispatch({ type: "SIGNOUT" });
}
return (
<>
Current user: {state.username}, isAuthenticated: {state.isAuth}
<button onClick={handleLogin}>Login</button>
<button onClick={handleSignout}>Signout</button>
</>
);
}
Hooks enable us to add custom functionality to our apps that suit our needs and can be combined with all the existing
hooks that we've covered
Hooks can also be included in third-party libraries for the sake of all React developers. There are many great React
libraries that provide custom hooks such as @apollo/client , react-query , swr and more.
/* Here is a custom React hook called useWindowSize that I wrote in order to calculate the window size (width and height) of any com
function changeWindowSize() {
setWindowSize({ width: window.innerWidth, height: window.innerHeight });
React.useEffect(() => {
window.addEventListener("resize", changeWindowSize);
return () => {
window.removeEventListener("resize", changeWindowSize);
};
}, []);
return windowSize;
}
/* To use the hook, we just need to import it where we need, call it, and use the width wherever we want to hide or show certain ele
// components/Header.js
function Header() {
const { width } = useWindowSize();
return (
<div>
{/* visible only when window greater than 500px */}
{width > 500 && <>Greater than 500px!</>}
{/* visible at any window size */}
<p>I'm always visible</p>
</div>
);
}
Rules of hooks
There are two essential rules of using React hooks that we cannot violate for them to work properly:
Hooks can only be used within function components (not plain JavaScript functions or class components)
Hooks can only be called at the top of components (they cannot be in conditionals, loops, or nested functions)
useEffect A function that adds the ability to perform side- // Let’s say we have a chat app with chatrooms.
effects from a function component. // This code sets up a listener to display chat
// messages we receive. With this code, we will
mount when a component is first added to a page // setup and then remove the listener on *every* render
unmount when a component is removed from the page useEffect(() => {
listenForMessagesFrom(chatroomId, addToMessageList);
return () => {
useEffect arguments stopListeningForMessagesFrom(chatroomId);
};
first argument The first argument to useEffect should be a });
function. Whatever happens in that function will get
run when the component mounts and after each render. // Same as above, except we’re passing an empty array as
// the second argument to useEffect, so we only add the
the return value of the first argument The first argu- // listener on the very first render, and only remove it
ment to useEffect can also, optionally, return a // right before unmounting. Much more efficient!
“cleanup” function. If it does, the returned function useEffect(() => {
will get run at the end of the render cycle, just before listenForMessagesFrom(chatroomId, addToMessageList);
the next render (or unmount) happens.
return () => {
stopListeningForMessagesFrom(chatroomId);
};
}, []); // we added the empty array on this line
// Show the alert after the first render only // THE FIX: use that second useEffect argument to
useEffect(() => { // prevent unnecessary useEffect calls
alert("This component just mounted!") const [count, setCount] = useState(0);
}, [])
useEffect(() => {
const savedCount = localStorage.get("savedCount");
// After first render AND after ‘someValue‘ changes setCount(savedCount);
useEffect(() => { // This little array argument right here is the fix!
alert("someValue just changed!") // Now this code will only run after the first render
}, [someValue]) }, []);
Using useEffect with React JS (16.8 and later) kickstartcoding.com A cheatsheet by Kickstart Coding
REACT, ROUTER, REDUX
React Example Code Defining components React Redux
React (15 and later) and Redux kickstartcoding.com A cheatsheet by Kickstart Coding
HTML & CSS (2021)
Common HTML Tags CSS Examples CSS Selectors