Skip to content
Laviyx Learn

Try

React Cheat Sheet

JSX, components and props, conditions and lists, state updates, events and forms, effects, refs, reducers, context, custom hooks, performance, TypeScript, routing and testing.

Updated

Project and entry point#

Terminal
npm create vite@latest my-app -- --template react      # or react-ts
cd my-app && npm install && npm run dev
JSX
// src/main.jsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.jsx";

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

JSX#

JSX
const name = "Asha";
const element = (
  <>
    <h1 className="title">Hello, {name}!</h1>
    <label htmlFor="q">Search</label>
    <input id="q" maxLength={40} />
    <p style={{ color: "teal", fontSize: 18 }}>Inline style object</p>
    {/* a comment */}
    <img src="/logo.png" alt="Logo" />
  </>
);

{ } holds expressions, not statements. Use className, htmlFor, camelCase attributes, self-closing tags, and one root (or a fragment <>...</>).

Components and props#

JSX
function Card({ title, price = 0, children }) {      // destructure, with a default
  return (
    <section>
      <h3>{title}</h3>
      <p>₹{price}</p>
      {children}
    </section>
  );
}

<Card title="Lamp" price={799}>
  <button>Buy</button>
</Card>;

Names start with a capital letter. Props are read-only. Strings in quotes, other values in braces.

Conditions and lists#

JSX
{isLoggedIn ? <Dashboard /> : <Login />}
{hasError && <p className="error">{message}</p>}
{count > 0 && <Badge count={count} />}             // not {count && ...}: 0 renders

{items.map((item) => (
  <li key={item.id}>{item.name}</li>                // stable, unique keys
))}

State#

JSX
import { useState } from "react";

const [count, setCount] = useState(0);
setCount(count + 1);
setCount((c) => c + 1);                             // updater: based on the latest value

const [user, setUser] = useState({ name: "Ravi", city: "Pune" });
setUser({ ...user, city: "Delhi" });                // copy, don't mutate

const [items, setItems] = useState([]);
setItems([...items, newItem]);                      // add
setItems(items.filter((i) => i.id !== id));         // remove
setItems(items.map((i) => (i.id === id ? { ...i, done: true } : i)));   // update

Events and forms#

JSX
<button onClick={handleClick}>Save</button>
<button onClick={() => remove(id)}>Delete</button>  // pass arguments with an arrow

<input value={text} onChange={(e) => setText(e.target.value)} />
<input type="checkbox" checked={agree} onChange={(e) => setAgree(e.target.checked)} />
<select value={plan} onChange={(e) => setPlan(e.target.value)}>...</select>

function handleSubmit(e) {
  e.preventDefault();                               // stop the page reload
}
<form onSubmit={handleSubmit}>...</form>

Effects#

JSX
import { useEffect } from "react";

useEffect(() => {
  const id = setInterval(tick, 1000);
  return () => clearInterval(id);                   // cleanup
}, []);                                             // [] once · [a, b] when they change · none: every render

useEffect(() => {
  let ignore = false;
  fetch(`/api/users/${userId}`)
    .then((res) => res.json())
    .then((data) => { if (!ignore) setUser(data); });
  return () => { ignore = true; };                  // ignore stale responses
}, [userId]);

Don't use effects to compute values from props or state; compute them during rendering.

Refs, reducers and context#

JSX
import { createContext, useContext, useReducer, useRef } from "react";

const inputRef = useRef(null);
<input ref={inputRef} />;
inputRef.current.focus();                           // in a handler or effect

function reducer(state, action) {
  switch (action.type) {
    case "added": return [...state, action.item];
    case "removed": return state.filter((i) => i.id !== action.id);
    default: throw new Error(`Unknown action: ${action.type}`);
  }
}
const [list, dispatch] = useReducer(reducer, []);
dispatch({ type: "added", item });

const ThemeContext = createContext("light");
<ThemeContext value="dark"><Page /></ThemeContext>;   // React 19 (older: ThemeContext.Provider)
const theme = useContext(ThemeContext);

Custom hooks#

JSX
function useToggle(initial = false) {
  const [on, setOn] = useState(initial);
  return [on, () => setOn((v) => !v)];
}
const [open, toggleOpen] = useToggle();

Names start with use. Each call has its own state. Hooks only at the top level of components and hooks.

Performance#

JSX
import { memo, useCallback, useMemo } from "react";

const Row = memo(function Row({ item, onSelect }) { /* ... */ });
const sorted = useMemo(() => items.toSorted(compare), [items]);
const handleSelect = useCallback((id) => setSelected(id), []);

Measure with the React DevTools Profiler first. The React Compiler can add memoisation automatically.

TypeScript#

TSX
import type { ChangeEvent, ReactNode } from "react";

type ButtonProps = { label: string; onClick?: () => void; children?: ReactNode };
function Button({ label, onClick }: ButtonProps) {
  return <button onClick={onClick}>{label}</button>;
}

const [user, setUser] = useState<User | null>(null);
const ref = useRef<HTMLInputElement>(null);
function handleChange(e: ChangeEvent<HTMLInputElement>) {}

Routing (React Router)#

JSX
import { BrowserRouter, Link, Route, Routes, useNavigate, useParams } from "react-router";

<BrowserRouter>
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="products/:id" element={<Product />} />
    <Route path="*" element={<NotFound />} />
  </Routes>
</BrowserRouter>;

<Link to="/products/3">Silk saree</Link>;
const { id } = useParams();                         // a string
const navigate = useNavigate(); navigate("/cart");

Testing (Vitest + Testing Library)#

JSX
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

test("increments", async () => {
  const user = userEvent.setup();
  render(<Counter />);
  await user.click(screen.getByRole("button", { name: "Increment" }));
  expect(screen.getByText("Count: 1")).toBeInTheDocument();
});

getBy must exist now · queryBy may be absent · findBy waits (async).

Common pitfalls#

  • onClick={fn()} calls the function during render; use onClick={fn} or onClick={() => fn(x)}.
  • Mutating state (push, obj.x = 1) doesn't re-render; create new objects and arrays.
  • Missing effect dependencies cause stale values; missing cleanup leaks timers and listeners.
  • Index or random keys break list updates; use stable ids.
  • Defining components inside components resets their state on every render.

Want the full explanations? Go to the Learn React course →