✓ Read this massive guide wherever suits you best. On the train, at your desk, standing in line... anywhere.

Note: There is limited coverage of class components in this cheatsheet. Class components are still valuable to know for existing React projects, but since the arrival of Hooks in 2018, we are able to make our apps with function components alone. I wanted to give beginners and experienced developers alike a Hooks-first approach treatment of React.

const year = 2020;
// we can insert primitive JS values in curly braces: {}
const greeting = <div>Hello React in {year}</div>;
// trying to insert objects will result in an error

JSX allows us to nest elements:

// to write JSX on multiple lines, wrap in parentheses: ()
const greeting = (
// div is the parent element
<div>
{/* h1 and p are child elements */}
<h1>Hello!</h1>
<p>Welcome to React</p>
</div>
);
// 'parents' and 'children' are how we describe JSX elements in relation
// to one another, like we would talk about HTML elements

HTML and JSX have a slightly different syntax:

// Empty div is not <div></div> (HTML), but <div/> (JSX)
<div/>
// A single tag element like input is not <input> (HTML), but <input/> (JSX)
<input name="email" />
// Attributes are written in camelcase for JSX (like JS variables
<button className="submit-button">Submit</button> // not 'class' (HTML)

The most basic React app requires three things:

ReactDOM.render() to render our app

A JSX element (called a root node in this context)

A DOM element within which to mount the app (usually a div with an id of root in an index.html file)

// What if we want to pass data to our component from a parent?
// I.e. to pass a user's name to display in our Header?
const username = "John";
// we add custom 'attributes' called props
ReactDOM.render(
<Header username={username} />,
document.getElementById("root")
);
// we called this prop 'username', but can use any valid JS identifier
// props is the object that every component receives as an argument
function Header(props) {
// the props we make on the component (i.e. username)
// become properties on the props object
return <h1>Hello {props.username}</h1>;
}

Props must never be directly changed (mutated):

// Components must ideally be 'pure' functions.
// That is, for every input, we be able to expect the same output
// we cannot do the following with props:
function Header(props) {
// we cannot mutate the props object, we can only read from it
props.username = "Doug";
return <h1>Hello {props.username}</h1>;
}
// But what if we want to modify a prop value that comes in?
// That's where we would use state (see the useState section)

Children props are useful if we want to pass elements / components as props to other components.

useState also gives us a 'setter' function to update the state it creates:

function App() {
// the setter function is always the second destructured value
const [language, setLanguage] = React.useState("python");
// the convention for the setter name is 'setStateVariable'
return (
<div>
{/* why use an arrow function here instead onClick={setterFn()} ? */}
<button onClick={() => setLanguage("javascript")}>
Change language to JS
</button>
{/* if not, setLanguage would be called immediately and not on click */}
<p>I am now learning {language}</p>
</div>
);
}
// note that whenever the setter function is called, the state updates,
// and the App component re-renders to display the new state

useState can be used once or multiple times within a single component:

// we have the option to organize state using whatever is the
// most appropriate data type, according to the data we're tracking
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
setDeveloper({ ...developer, yearsExperience: years });
}
return (
<div>
{/* no need to get prev 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 the new state depends on the previous state, to guarantee that the update is done reliably, we can use a function within the setter function that gives us the correct previous state.

Side effects and useEffect

useEffect lets us perform side effects in function components. So what are side effects?

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 caused 'side effects').

useEffect accepts a callback function (called the 'effect' function), which will by default run every time there is a re-render. It runs once our component mounts, which is the right time to perform a side effect in the component lifecycle.

To avoid executing the effect callback after each render, we provide a second argument, an empty array:

function App() {
...
// now our button doesn't work no matter how many times we click it...
useEffect(() => {
document.body.style.backgroundColor = colors[colorIndex];
}, []);
// the background color is only set once, upon mount
// how do we not have the effect function run for every state update...
// but still have it work whenever the button is clicked?
return (
<button onClick={handleChangeIndex}>
Change background color
</button>
);
}

useEffect lets us conditionally perform effects with the dependencies array.

The dependencies array is the second argument, and if any one of the values in the array changes, the effect function runs again.

Performance and useCallback

useCallback is a hook that is used for improving our component's performance.

If you have a component that re-renders frequently, useCallback prevents callback functions within the component from being recreated every single time the component re-renders (which means the function component re-runs).

Memoization and useMemo

useMemo is very similar to useCallback and is for improving performance. But instead of being for callbacks, it is for storing the results of expensive calculations.

useMemo allows us to 'memoize', or remember the result of expensive calculations when they have already been made for certain inputs (we already did it once for these values, so it's nothing new to do it again).

useMemo returns a value from the computation, not a callback function (but can be a function).

Reducers and useReducer

Reducers are simple, predictable (pure) functions that take a previous state object and an action object and return a new state object. For example:

// let's say this reducer manages user state in our app:
function reducer(state, action) {
// reducers often use a switch statement to update state
// in one way or another based on the action's type property
switch (action.type) {
// if action.type has the string 'LOGIN' on it
case "LOGIN":
// we get data from the payload object on action
return { username: action.payload.username, isAuth: true };
case "SIGNOUT":
return { username: "", isAuth: false };
default:
// if no case matches, return previous state
return state;
}
}

Reducers are a powerful pattern for managing state that is used in the popular state management library Redux (common 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.

Thus useReducer + useContext can be an entire state management system for our apps.

Our mission: to help people learn to code for free. We accomplish this by creating thousands of
videos, articles, and interactive coding lessons - all freely available to the public. We also have
thousands of freeCodeCamp study groups around the world.

Donations to freeCodeCamp go toward our education initiatives, and help pay for servers, services,
and staff.