The React Cheatsheet
The React Cheatsheet
React Fundamentals
JSX Elements
React applications are structured using a syntax called JSX. This is the syntax of a basic JSX element
/*
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 JavaScript, such
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 valid (/
*/
/*
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 trailing fo
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 = (
// div is the parent element
<div>
{/* h1 and p are child elements */}
<h1>Hello!</h1>
<p>Welcome to React</p>
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>
);
1. ReactDOM.render() : used to render (show) our app by mounting it onto an HTML element
2. A JSX element: called a "root node", because it is the root of our application. Meaning, rendering it will render all children within
it
3. 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 {
render() {
return <h1>Hello React</h1>;
}
}
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'
*/
ReactDOM.render(
<Header username={username} />,
document.getElementById("root")
);
// We called this prop 'username', but can use any valid identifier we would give, for example, a JavaScript variable
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();
/* if user is authenticated, show the authenticated app, otherwise, the unauthenticated app */
if (isAuthenticated) {
return <AuthenticatedApp />;
} else {
/* alternatively, we can drop the else section and provide a simple return, and the conditional will operate in the same way */
return <UnAuthenticatedApp />;
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"];
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
/*
The convention for most event handler functions is to prefix them with the word 'handle' and then the action they perform (i.e. handleToggle
*/
function handleToggleTheme() {
// code to toggle app theme
}
/* In HTML, onclick is all lowercase, plus the event handler includes a set of parentheses after being referenced */
<button onclick="handleToggleTheme()">
Toggle Theme
/*
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 reload the pa
*/
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
Local component state is managed by the React hook useState which gives us both a state variable and a function that allows
us to update it
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?
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
</button>
<input
type="number"
value={yearsExperience}
onChange={(event) => setYearsExperience(event.target.value)}
/>
<p>I am now learning {language}</p>
<p>I have {yearsExperience} years of experience</p>
</div>
);
}
/* 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 (
<button onClick={handleToggleEmployment}>Toggle Employment Status</button>
);
}
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, goes back
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);
const colors = ["blue", "green", "red", "orange"];
/*
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]);
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())
.then((data) => setUser(data));
}, []);
}
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)
React.memo helps most with preventing lists of components from being re-rendered when their parent components re-render
/*
In the following application, we are keeping track of our programming skills. We can create new skills using an input, they are added to the
*/
function App() {
const [skill, setSkill] = React.useState("");
const [skills, setSkills] = React.useState(["HTML", "CSS", "JavaScript"]);
function handleChangeInput(event) {
setSkill(event.target.value);
}
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 (App) re-
/* However, once we wrap the SkillList component in React.memo (which is a higher-order function, meaning it accepts a function as an argume
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 we clic
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 broken!
What is happening is the handleRemoveSkill callback function is being recreated everytime App is rerendered, causing all children to be re
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 can add
*/
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 (
<>
<SearchSkills skills={skills} />
<input onChange={handleChangeInput} />
<button onClick={handleAddSkill}>Add Skill</button>
<SkillList skills={skills} handleRemoveSkill={handleRemoveSkill} />
</>
);
}
// /* 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 skills t
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 changes */
const searchResults = React.useMemo(() => {
return skills.filter((s) => s.includes(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 doesn't
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
We call this 'UserContext' because we're passing down user data
*/
const UserContext = React.createContext();
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 are a powerful pattern for managing state that is used in the popular state management library Redux (commonly
used with React)
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 component i
function changeWindowSize() {
setWindowSize({ width: window.innerWidth, height: window.innerHeight });
}
React.useEffect(() => {
window.addEventListener("resize", changeWindowSize);
return () => {
window.removeEventListener("resize", changeWindowSize);
};
}, []);
return windowSize;
}
// 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)