Frontend React and React Native cheat sheet
React for the web and React Native on a few screens: which hook to reach for, the effect traps that cause most bugs, server data, forms, an accessibility checklist, performance fixes, Testing Library queries and the mobile specifics. Keep it open while you build or review a screen.
The mental model in five lines
A component is a function from props and state to UI. Props come from the parent and are read-only; state belongs to the component and changes only through its setter. Every state change re-renders that component and its children. Effects are for syncing with things outside React (the network, timers, subscriptions), not for computing values. Data flows down, events flow up through callbacks (Components, State and effects).
Which hook? "I want to..."
Hooks run in the same order on every render. Call them at the top level of a component or a custom hook, never inside conditions, loops or after an early return.
| I want to... | Reach for | Watch out for | Lesson |
|---|---|---|---|
| Remember a value that changes the UI | useState | Use the updater form setQty(q => q + 1) when the new value depends on the old one | State and effects |
| Handle state with many related transitions | useReducer | Keep the reducer pure; no fetches inside it | State and effects |
| Show a value computed from props or state | A plain variable during render | Do not copy it into state and sync it with an effect | State and effects |
| Skip an expensive calculation on re-render | useMemo | Only after measuring; the React Compiler can do this for you where enabled | Accessible and fast |
| Keep a callback identity stable for a memoized child | useCallback | Useless unless the child is wrapped in memo or uses it as a dependency | Accessible and fast |
| Hold a value that does not re-render (timer id, DOM node) | useRef | Do not read or write ref.current during render | State and effects |
| Sync with something outside React | useEffect with a cleanup | List every value it reads in the dependency array | State and effects |
| Measure layout before the browser paints | useLayoutEffect | Blocks paint; rare | Accessible and fast |
| Share a value deep in the tree (theme, user, store id) | createContext and useContext | Every consumer re-renders when the value changes; split contexts or memoize the value | Components |
| Keep typing smooth while a big list filters | useDeferredValue or useTransition | Marks work as non-urgent; does not make it faster | Accessible and fast |
| Make ids for label and input pairs | useId | Not for list keys | Forms |
| Reuse stateful logic in two components | A custom hook named useSomething | Each caller gets its own state; hooks share logic, not data | Components |
| Read a form action's pending state and result | useActionState, useFormStatus (React 19) | React 19 only; older code uses useState flags | Forms |
| Show the result before the server confirms | useOptimistic (React 19) | Roll back and tell the user when the request fails | Server data |
Effect traps: if you see X, it is probably Y
Most React bugs on Mangrove's Store app and Back office came from effects. Ask first: "Is this syncing with something outside React?" If not, it should not be an effect at all.
| You see | Likely cause | Fix | Lesson |
|---|---|---|---|
| Infinite render loop, browser freezes | An effect sets state that is in its own dependency array, or a new object or array in the deps on every render | Compute during render; move the object inside the effect; depend on primitive values | State and effects |
| Screen shows stale data from the previous store | Missing dependency, so the effect never re-ran | Add the dependency; turn on the react-hooks/exhaustive-deps lint rule | State and effects |
| Results for S-014 replaced by results for S-102 | Race: an older request finished last | Ignore stale responses with a flag or AbortController in the cleanup, or use a data library | Server data |
| Effect runs twice in development | React Strict Mode mounts, unmounts and mounts again on purpose | Write a cleanup that undoes the effect; do not disable Strict Mode | State and effects |
| Memory leak warning, timers keep firing | No cleanup for an interval, listener or subscription | Return a function that clears it | State and effects |
| Input loses focus or state resets on every keystroke | A component defined inside another component, or a changing key | Define components at module level; keep keys stable | Components |
| Wrong row updates after deleting from a list | Array index used as key | Use a stable id such as the SKU or order id | Components |
| State change does not show | Mutated the object or array in place | Create a new one: [...items, x], { ...order, status } | State and effects |
Value is one step behind right after setX | State updates apply on the next render | Use the value you passed to the setter, or the updater form | State and effects |
| Hydration mismatch warning | Server and client render different output (dates, Math.random, window) | Render the same on both; move client-only parts into an effect | Accessible and fast |
A fetch effect done right
Only if you are not using a data library. The cleanup cancels the request when the store changes or the screen closes, so an old answer never overwrites a new one.
useEffect(() => {
const ctrl = new AbortController();
setStatus('loading');
fetch(`/api/stores/${storeId}/stock`, { signal: ctrl.signal })
.then((r) => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); })
.then((data) => { setStock(data); setStatus('ready'); })
.catch((e) => { if (e.name !== 'AbortError') setStatus('error'); });
return () => ctrl.abort();
}, [storeId]);Server data
Server data is a cache of someone else's state, not your state. A query library (TanStack Query, SWR, or your framework's loader) gives you caching, deduplication, retries and refetch on focus for free (Server data).
| I want to... | Do this | Lesson |
|---|---|---|
| Load a list and show loading, error and empty states | A query keyed by everything it depends on, such as ['stock', storeId, page]; render all four states | Server data |
| Refresh the list after a change | Invalidate that query key after the mutation succeeds | Server data |
| Avoid a spinner flash on fast networks | Keep previous data while the next page loads; show a small inline indicator | Server data |
| Never charge twice on a double tap | Disable the button while pending, and send an idempotency key the server checks | Server data, Auth and limits |
| Show money | Keep integer cents from the API; format only at the edge with Intl.NumberFormat | Data and money |
| Type the API response | Generate types from the OpenAPI spec, or validate with a schema library at the boundary | TypeScript |
Forms
Controlled inputs (value plus onChange) suit small forms and live validation. Uncontrolled inputs read with FormData on submit suit long forms. Either way the server validates again: the browser check is for the user, the server check is for safety (Forms).
| Rule | Why | Lesson |
|---|---|---|
Every input has a visible <label> tied by htmlFor and id | Screen readers announce it; tapping the label focuses the field | Forms |
Use the right type and inputMode (email, tel, inputMode="decimal") | Phones show the right keyboard | Forms |
Set autoComplete (name, street-address, one-time-code) | Fewer keystrokes, fewer typos | Forms |
| Validate on blur and submit, not on every keystroke | Errors do not shout while the user is still typing | Forms |
Put the error next to the field and link it with aria-describedby; move focus to the first error | The user sees and hears what to fix | Forms, Accessibility |
| Keep the user's input after a failed submit | Retyping a 12-line Partner app order is how users give up | Forms |
| Disable submit only while pending, not while invalid | A disabled button gives no reason; an error message does | Forms |
Accessibility checklist
Run it on every new screen. Most items take minutes when done early and days when done after launch (Accessible and fast, Accessibility).
- Use real elements:
<button>for actions,<a href>for navigation, headings in order,<ul>for lists. AdivwithonClickis not reachable by keyboard. - Everything works with Tab, Shift+Tab, Enter, Space and Escape, and the focus ring stays visible.
- Images that carry meaning have
alttext; decorative ones havealt="". Icon-only buttons have anaria-label. - Text contrast at least 4.5:1 (3:1 for large text), per WCAG 2.x AA. Never use colour alone for status; add a word or icon.
- Touch targets at least 44 by 44 points on iOS and 48 by 48 dp on Android.
- Dialogs trap focus, close on Escape and return focus to the button that opened them.
- Announce async results (such as "Order saved") with a live region,
aria-live="polite". - Respect
prefers-reduced-motionand the user's text size; layouts survive 200% zoom. - First rule of ARIA: if a native element does the job, use it instead of ARIA.
Performance: if you see X, reach for Y
Measure first with the React DevTools Profiler and the browser Performance panel, on a mid-range phone, not a laptop (Accessible and fast, Performance).
| You see | Reach for first | Then, if needed | Lesson |
|---|---|---|---|
| Typing lags in a search box | Move state down so only the box re-renders | useDeferredValue for the filtered list | Accessible and fast |
| Long list of 25,000 SKUs is slow | Paginate or virtualize (render only visible rows) | memo on the row component | Accessible and fast |
| Whole page re-renders on a small change | Split context; lift content up as children | memo with stable props | Accessible and fast |
| Slow first load | Code-split routes with lazy and Suspense; check bundle size | Server rendering; drop heavy dependencies | Performance |
| Layout jumps as images load | Set image width and height | Reserve space for late content | Accessible and fast |
| Big images on a phone | Responsive sizes and a modern format, lazy load below the fold | A CDN that resizes on the fly | Performance |
Web vitals worth remembering
Google's "good" thresholds, measured at the 75th percentile of real page loads: Largest Contentful Paint (LCP) 2.5 s or less, Interaction to Next Paint (INP) 200 ms or less, Cumulative Layout Shift (CLS) 0.1 or less. INP replaced First Input Delay in 2024 (Accessible and fast, Performance).
Testing with Testing Library
Test what the user sees and does, not component internals. Query by role first; if you cannot find an element by role and name, a screen reader user cannot either (Testing React, Test and release).
| I want to... | Use | Lesson |
|---|---|---|
| Find a button, link, heading or input | screen.getByRole('button', { name: /place order/i }) | Testing React |
| Find a form field | getByLabelText('Quantity') | Testing React |
| Check something is not there | queryByText(...) returns null instead of throwing | Testing React |
| Wait for something that appears after a fetch | await screen.findByText('Saved') | Testing React |
| Type and click like a user | const user = userEvent.setup(); await user.type(input, '12'); await user.click(btn) | Testing React |
| Fake the API | Mock at the network level (for example Mock Service Worker), not by mocking your own hooks | Testing React |
| Last resort selector | getByTestId; on React Native, testID | Test and release |
| Test whole flows in a real browser | Playwright end-to-end tests for the few journeys that make money | Playwright E2E |
React Native: web element to core component
React Native renders native views, not HTML. There is no CSS cascade: styles are JavaScript objects, layout is Flexbox with flexDirection: 'column' as the default, and all text must sit inside <Text> (RN basics).
| On the web | In React Native | Notes | Lesson |
|---|---|---|---|
<div> | <View> | Flexbox, column by default | RN basics |
<span>, <p> | <Text> | Raw strings outside Text crash | RN basics |
<img> | <Image> | Network images need a width and height | RN basics |
<input> | <TextInput> | onChangeText, keyboardType | RN basics |
<button> | <Pressable> | Add accessibilityRole="button" and a label | RN basics |
Scrolling <div> | <ScrollView> | Renders every child; only for short content | Mobile performance |
| A long list | <FlatList> or <SectionList> | Virtualized; give keyExtractor a stable id | Mobile performance |
localStorage | AsyncStorage for small values, SQLite for real data, secure storage for tokens | Never keep tokens in plain AsyncStorage | Offline first |
| URL routing | A navigator (React Navigation or Expo Router) | Stacks, tabs, deep links | Navigation |
React Native: "I want to..."
The Mangrove app and the Rider app both run on React Native with Expo. Check which Expo SDK and React Native version a library supports before adding it.
| I want to... | Do this | Lesson |
|---|---|---|
| Keep content out of the notch and home indicator | Safe area insets (react-native-safe-area-context) | RN basics |
| Stop the keyboard covering the input | KeyboardAvoidingView and a scroll container | RN basics |
| Open the app at a product from a link | Deep links with a URL scheme, plus universal links (iOS) and app links (Android) | Navigation |
| Keep the cart when the rider loses signal | Save to a local database first, queue writes in an outbox, sync with idempotency keys | Offline first |
| Know if the device is online | NetInfo, but treat it as a hint; a failed request is the real signal | Offline first |
| Use camera, location or notifications | Ask for permission at the moment of use, explain why, handle "denied" gracefully | Device features |
| Scan a barcode | A camera library with barcode scanning; test in bad light | Device features |
| Send push notifications | APNs for iOS, FCM for Android (or Expo's push service on top); store tokens per device | Device features |
| Find what makes a screen janky | Profile in a release build on a cheap Android phone; check the JS and UI frame rates | Mobile performance |
| Ship a JavaScript-only fix fast | Over-the-air update (for example EAS Update), within the store rules; native changes need a store release | Test and release |
| Roll out a store release safely | Staged rollout on Google Play, phased release on the App Store; watch crash-free sessions | Test and release |
Mobile numbers worth remembering
60 frames per second leaves about 16 ms per frame for JavaScript and layout; 120 Hz screens leave about 8 ms. Plan for slow 3G-like networks and phones with 2 to 3 GB of RAM in many markets. A crash-free sessions rate below about 99.5% usually means users notice. Store review can take from hours to a few days, so never plan a launch for the day you submit (Mobile performance, Test and release).
Glossary
Plain definitions of the terms used most in the React and React Native courses.
- Component
- A function that takes props and returns UI; the building block of every React screen. Components
- Props
- Read-only inputs a parent passes to a child component. Components
- State
- Data a component owns that changes over time; changing it re-renders the component. State and effects
- Re-render
- React calling a component again to work out the new UI, then updating only what changed. State and effects
- Hook
- A function starting with
usethat lets a component use React features such as state or effects. State and effects - Effect
- Code that runs after render to sync with something outside React, with an optional cleanup. State and effects
- Dependency array
- The list of values an effect or memo reads; React re-runs it when one of them changes. State and effects
- Key
- A stable id on each list item that lets React match items between renders. Components
- Lifting state up
- Moving state to the closest common parent so two children can share it. Components
- Context
- A way to pass a value deep down the tree without passing props through every level. Components
- Controlled input
- An input whose value comes from React state and changes only through
onChange. Forms - Memoization
- Reusing a previous result when inputs have not changed (
memo,useMemo,useCallback). Accessible and fast - Suspense
- A component that shows a fallback while its children wait for code or data. Server data
- Hydration
- Attaching React to HTML that was rendered on the server, so the page becomes interactive. Accessible and fast
- Optimistic update
- Showing the expected result before the server confirms, and undoing it if the request fails. Server data
- Cache invalidation
- Marking cached server data as stale so it is fetched again. Server data
- Strict Mode
- A development-only wrapper that runs some code twice to expose missing cleanups and impure renders. State and effects
- ARIA
- Attributes that tell assistive technology what an element is and its state; a fallback when no native element fits. Accessibility
- Virtualized list
- A list that renders only the rows on screen, such as React Native's
FlatList. Mobile performance - Core components
- React Native's built-in native building blocks:
View,Text,Image,TextInput,Pressableand lists. RN basics - Expo
- A framework and set of services on top of React Native for building, updating and releasing apps. RN basics
- Deep link
- A URL that opens the app at a specific screen, such as one product. Navigation
- Offline-first
- Saving to the device first and syncing with the server when the network allows. Offline first
- Over-the-air (OTA) update
- Shipping a new JavaScript bundle to installed apps without a store release. Test and release
- Staged rollout
- Releasing a new app version to a growing share of users while watching crash rates. Test and release
- Core Web Vitals
- Google's three user-experience metrics: LCP for loading, INP for responsiveness, CLS for visual stability. Performance
Courses on Kolom
- React for the Web: components, state, server data, forms, accessibility and testing, built on Mangrove's Back office.
- Mobile Apps with React Native: navigation, offline-first carts, device features and store releases for the Mangrove app.
- JavaScript in the Real World: browser APIs, TypeScript and performance under the frameworks.
- Software Testing and QA: how testers look at your screens, including accessibility checks.
- Test Automation and Quality Engineering: Playwright end-to-end tests and stable CI.
What to read next
- The React documentation at react.dev. Read "You Might Not Need an Effect" and "Thinking in React" first; they prevent most of the bugs in the traps table.
- The React Native documentation at reactnative.dev, and the Expo docs at docs.expo.dev for builds, updates and device APIs.
- Testing Library at testing-library.com, especially the guide on which query to use.
- web.dev at web.dev for Core Web Vitals and how to measure them.
- MDN Web Docs on accessibility at developer.mozilla.org, and the WCAG quick reference from the W3C.