React Native Practice
React Native, built the way real mobile teams build it. Learn to think in components, screens, navigation, and reusable mobile patterns as you build interfaces that stay maintainable as they grow. You'll start with components, props, state, and styling, then move through hooks, forms, lists, navigation, platform APIs, animations, data fetching, state management, and the patterns production mobile apps are built on. Every exercise is a small piece of a real mobile interface — not toy syntax drills — so you learn not just how React Native works, but how to make the design and architecture decisions good mobile engineers make every day. Finish able to build, structure, debug, and reason about React Native applications with confidence.
Core Components & Styling
The layer of a React Native app that turns props into pixels using React Native core components — View, Text, Image, and Pressable — laid out with Flexbox and styled through StyleSheet.create. Styles compose through arrays and are toggled through conditions and the Pressable style callback, never rebuilt as inline objects on every render. Missing values render as safe placeholders rather than surfacing as raw null, undefined, or NaN.
Payment Transaction Row
A reusable transaction row for a retail banking account activity screen. Formats an integer amount in minor currency units into a signed two-decimal string, applies a distinct debit / credit / zero style based on sign, and layers a muted "pending" style over the base amount style through a conditional style array — never an inline style object at render time.
Patient Vitals Card
A reusable vitals card for a hospital ward observations screen. Lays out the patient header as a row where the name flexes and the bed number pins to the right, renders the three vitals as equal-width columns, prints a dash for any reading not yet taken (never showing null, undefined, or NaN), and names the recording clinician in a footer.
Menu Dish Card
A reusable dish card for a restaurant menu screen. Renders a bundled photo with resizeMode "cover" and a fixed aspectRatio (falling back to a bundled placeholder when the photo prop is absent), nests the description inside the name so it inherits the outer text style while carrying its own, formats the price from minor units, renders one badge per dietary tag, and dims — never hides — an unavailable dish.
Job Application Status Card
A card for a recruitment platform's applications screen. Shows the role title, employer, and last updated date; renders a stage badge with its own label and appearance per stage (APPLIED, SCREENING, INTERVIEW, OFFER, REJECTED); exposes a Pressable withdraw control that reports the application id up to the parent and shows a pressed style through the style callback; and treats OFFER and REJECTED as terminal — the withdraw button is disabled and the card is dimmed while staying visible.
Touchables & Press Interaction
How a React Native app responds to touch. Pressable is the core primitive — press, long press, disabled, pressed feedback through the style callback, hitSlop for larger touch targets, and android_ripple versus iOS pressed opacity for platform-appropriate feedback. Interactive elements carry accessibility roles, labels, and selected / disabled state. Controls are typically controlled — behaviour comes from props and callbacks, not from hidden internal state — and richer interactions layer hook-driven side effects like timers on top, with cleanup on release, expiry, and unmount.
Warehouse Pick Confirm
Build a warehouse picking control with quantity adjustment, short-pick handling, and confirmation interactions.
Delivery Rating Strip
Create a delivery rating component with accessible star selection and conditional follow-up feedback.
Insurance Claim Line Row
Build an interactive insurance claim row with lifecycle-aware actions and derived customer contribution.
Clinic Appointment Slot Board
Build a clinic appointment slot board with exclusive slot holds, countdown expiry, and derived availability rules.
State & Component Composition
A cluster of four assessments that check the fundamentals of React state and composition in a mobile UI — using `useState` in the right owner, driving stateless children through props / callbacks / slots, deriving every read-only value on each render rather than mirroring it into state, and updating state immutably. Each problem isolates one small business control (dispensing, allocating, reporting, signing in) so the composition and derivation choices are what pass or fail the tests, not the domain.
Pharmacy Dispense Counter
A pharmacist's control that builds a prescribed quantity from the pack sizes actually stocked. Derives the dispensed total, units still owed, and whether the prescription can be met exactly from the packs on hand — flagging over-dispense separately from owing.
Salary Deposit Allocation
An employee's control for splitting net pay across several linked accounts. Nudging one percentage stepper redistributes the delta across the others in proportion, with a documented rounding rule so two identical sessions produce the same numbers.
Field Service Report Section
A reusable collapsible section shell for the field engineer's report screens. Owns its own expanded/collapsed `useState`, unmounts children on collapse rather than hiding them, exposes a title bar with a live child-count badge plus optional `action`, `footer`, and `empty` slots, and composes children opaquely without inspecting or cloning them.
Site Crew Sign-In
A supervisor's sign-in screen for a construction site — the roster renders in a `FlatList` keyed by worker id, each row toggles the worker between on-site and absent, and any worker whose required certification has lapsed or is missing stays blocked with the reason shown. Header keeps live counts and a per-trade shortfall against the day's minimum crew.
TextInput & Forms
The input layer of a React Native app. TextInput values are controlled — held on the component that owns them — and non-numeric or oversize input is filtered before it ever reaches state. Every derived value (weighted total, letter grade, refundable ceiling, character count, submit-disabled flag) is recomputed on each render rather than mirrored into a second useState. Validation runs on blur and on submit, never on every keystroke, and each rule surfaces its own message on its own field. Keyboard-driven focus chains use useRef with returnKeyType and blurOnSubmit, and KeyboardAvoidingView selects its behaviour per platform.
Course Grade Entry
A tutor's grade entry screen. A controlled numeric TextInput per assessment component holds its value as a string so a cleared field stays empty rather than becoming zero; non-numeric characters never reach state. The weighted total and letter grade update live from the entered marks and their weightings and are derived on every render — never mirrored into state. Save is blocked with its own message on any empty or over-max field, a mark exactly at maxMark is accepted, and a total narrowly below a grade boundary warns for moderation without blocking.
Contractor Onboarding Form
A staffing agency's onboarding form. Four controlled inputs — full name, payroll reference, assignment end date (YYYY-MM-DD), right-to-work expiry — validated only on blur or on submit, never on every keystroke. Assignment end must land within a fixed horizon from today; right-to-work expiry must be on or after the assignment end. Each rule surfaces its own message on its own field, submit unlocks only when every field is valid, and a corrected field clears its own message without touching any other field's.
Consultation Note Entry
A clinician's consultation note screen. Four fields chain together so pressing "next" on the keyboard moves focus down through useRef refs with blurOnSubmit false, and the last field blurs the keyboard instead. The multiline notes area uses numberOfLines and textAlignVertical for top-aligned long text, shows a live character count against the record limit, and hard-caps entry at the limit — including on a paste. KeyboardAvoidingView selects padding on iOS and height on Android so the keyboard never occludes the notes area.
Merchant Refund Request
A merchant refund request form. The refundable ceiling is derived from the original transaction less what has already been refunded; the amount is validated against the ceiling and classified as full (exactly at ceiling) or partial (below); the reason list is filtered by the classification and a previously-selected code no longer offered is cleared. useWindowDimensions drives a one-vs-two-column layout at a documented breakpoint while preserving entered values across a resize (no remount); Platform.select branches the amount keyboardType and the submit style.
Platform & Responsive Layout
The layer of a React Native app that adapts to platform and screen. Platform-branching visual properties (header height, alignment, iOS shadow vs Android elevation, keyboard type) live inside StyleSheet.create + Platform.select rather than as branches in the JSX tree, so the returned elements are the same on both platforms. Layout adapts to useWindowDimensions — column counts derived against a minimum tile width with leftover width in the gutters, compact-vs-full variants at a documented height threshold, and orientation-driven conditional rendering — without remounting on a resize. Cards hold their shape through aspectRatio rather than a fixed height, and a flexible spacer absorbs remaining vertical space so fixed-shape content cannot grow past its intent. FlatList carries a key that changes on column-count change and a getItemLayout derived from the same row height so scrollToIndex lands exactly.
Network Status Header
A telecoms operator's service status header. A list of open incidents collapses to one headline state by severity precedence (any outage wins over any degradation, both win over all-clear); only the winning severity's count is shown, and an empty array renders the all-clear state. The header's height, title alignment and iOS-shadow-vs-Android-elevation live inside a single StyleSheet.create block using Platform.select — the JSX tree is identical on both platforms. A last-checked label reads relative below a documented age threshold and switches to an absolute clock time above it, derived from a passed _now_ so tests can exercise the switch without mocking the clock.
Delivery Slot Grid
A grocery delivery slot picker where whole tiles fit the width without ever being stretched past their maximum. Column count is derived from useWindowDimensions().width against a minimum tile width; leftover width lands in the gutters rather than in the tiles, and the derived tile width is capped at a documented maximum. The grid renders through FlatList's numColumns with a key that changes when the column count changes so React re-creates the list cleanly on reflow — no tile is dropped or duplicated. Per-slot state (full / scarce / ok) derives from capacity against a scarcity threshold; a surcharge chip renders only when non-zero; a price column renders only in landscape.
Digital Boarding Pass
A digital boarding pass a passenger holds up at the gate — readable on a small phone or a large one. The pass card holds a fixed aspectRatio (never a fixed height) so its shape is the same at every screen size; a boarding-group strip highlights the passenger's own group and dims groups already called; a flexible spacer between the card and the footer keeps the footer pinned above the safe-area inset without ever letting the card grow. useWindowDimensions switches to a compact layout below a documented height threshold — the card shrinks but keeps the aspect ratio, and the group strip collapses to a single line.
Hotel Rate Calendar
A nightly rate calendar in a hotel booking app where rates and availability depend on the stay length. Every night in the season renders through a single FlatList whose numColumns and rowHeight are derived from useWindowDimensions, and getItemLayout is derived from the same rowHeight so scrollToIndex lands exactly on the requested night without measuring. Each night shows the rate for the currently selected stayLength looked up from a length-of-stay rate table (with a documented fallback to the largest key at or below the current stayLength); a night whose minimum-stay requirement exceeds the selection renders disabled with the required nights shown in place of the rate. Platform.select supplies the header treatment inside a single StyleSheet.create block. renderItem is memoised so unrelated re-renders don't rebuild the row layout — only a stayLength or dimension change does.
Advanced Lists & Virtualisation
The virtualisation layer of a React Native app. Long, scrollable content renders through FlatList or SectionList with getItemLayout derived from a known row height so the list can position rows without measuring — enabling initialScrollIndex, scrollToIndex, and scrollToLocation to land exactly. Rows are React.memo'd and renderItem is useCallback-memoised so an unrelated re-render does not rebuild any row, and sections and derived data are useMemo'd so their identity is stable. onViewableItemsChanged is held in a useRef so its identity never changes (RN throws otherwise) and drives sticky-header and in-view derivations from the reported viewable items. Screens navigate through prop-injected navigation and keep the list mounted behind an overlay so returning does not remount a single row.
Medication Adherence Calendar
A chronic-care adherence calendar. A full year of dosing days scrolls through a virtualised FlatList opened on today via initialScrollIndex, getItemLayout is derived from a fixed row height so scrollToIndex lands exactly on any date, and a sticky month header follows the topmost viewable day via onViewableItemsChanged held in a useRef. Rows are React.memo'd; tapping a day opens a detail sheet listing that day's doses due, taken and missed — without re-rendering any other row in the list.
Trade Show Session Schedule
A conference schedule screen for a trade-show app. Sessions are grouped by hall in a SectionList with sticky section headers via a memoised sections array built from buildSections, jump-to-hall chips call scrollToLocation with the section index looked up from sectionIndexForHall, and an in-session banner names the first viewable session via firstViewableSession. The onViewableItemsChanged callback is held in a useRef so its identity never changes across unrelated re-renders — React Native throws otherwise.
Dealer Stock Directory
A used-vehicle stock directory for a dealership app. The catalogue loads via fetch from a configurable apiUrl into a virtualised FlatList with getItemLayout and React.memo'd rows; renderItem is useCallback-memoised so an unrelated re-render leaves its identity unchanged. Make / price / mileage filters compose to narrow the visible set, and a row that stays visible after a filter change does not remount. Tapping a vehicle calls prop-injected navigation.navigate('Detail', { id }) — the detail overlay renders only when route.name === 'Detail' and the list stays mounted behind it so returning does not remount a single row.
Navigation with React Navigation
The navigation layer of a React Native app built on @react-navigation. Native stacks with route params carrying the id, dynamic header options set through navigation.setOptions (title and headerRight) from screen state, and repeated push calls that stack fresh screen entries instead of collapsing to one via navigate. Bottom tabs wrap per-tab nested stacks whose position is preserved across tab switches (never opting out with unmountOnBlur), and a custom tab bar reads badge counts from a shared Context and handles the tabPress event so pressing the focused tab pops that stack to its root. Auth vs service stacks are chosen conditionally from a session held in Context — never navigated to imperatively — and role-gated screens are absent from the navigator entirely for roles that do not have them. Async work inside a screen is focus-scoped via useFocusEffect with an AbortController that cancels the in-flight request cleanly on blur / unmount, and every focus re-fetches.
Property Viewing Flow
An estate-agent property viewing flow. Native Stack.Navigator with Listings → Property → RoomNote screens, route params carrying the listing reference, a dynamic header title set via navigation.setOptions once findPropertyByRef resolves, a header-right publish control disabled until the property loads, repeated navigation.push calls that stack a fresh RoomNote for every room the agent walks into (nextRoomNumber increments the counter), and a one-shot navigation.popToTop back to the listings queue.
Restaurant Manager Access
A restaurant service app where floor staff and managers see different screens depending on session and role. RootNavigator reads a session from SessionContext and picks a stack via pickStackFor — auth stack when null, server stack for role 'server', manager stack for role 'manager'. Manager-only routes are registered only in the manager stack — hasRoute reflects the same lookup — so a server never has the Voids & Discounts route at all. Sign-in dispatches signIn({ role }); sign-out clears the session and by conditional render unmounts the service stack entirely, leaving no back entry.
Travel Companion Hub
A travel companion hub screen. Bottom Tab.Navigator with Itinerary, Bookings and Expenses, each wrapping its own nested native Stack.Navigator, a custom HubTabBar that reads badge counts from CountsContext via computeBadges (and hides zero counts), and press behaviour that emits a tabPress event before navigating — pressing a non-focused tab navigates to it, pressing the focused tab pops that tab's nested stack to root. Inactive tabs preserve their nested stack state across switches — Tab.Navigator's default behaviour, never opted out of with unmountOnBlur.
Courier Stop Detail
The stop screen in a courier delivery app. Native stack with Route → Stop; on focus StopScreen fires fetchImpl(buildStopUrl(stopId), { signal }) via useFocusEffect and passes the AbortController's signal, aborting the in-flight request cleanly on blur / unmount. Returning to the screen refetches — no caching. A stopReducer holds loading / loaded / error states, error state shows a Try again button that re-issues exactly one further request, and the injectable createApiClient factory serves fixtures with configurable failuresBeforeSuccess so tests never touch the network. Star problem — accumulates the route-params + push topics of 10231, the tab-preserving-position topic of 10232 and the declarative-context-driven navigation topic of 10233, and lands them on the focus-scoped async pattern that every real navigation-driven data fetch needs.
Data Fetching & Async State
The async layer of a React Native app. Requests are debounced so rapid input collapses to one call per pause, superseded requests are cancelled with AbortController, and stale responses are discarded via a monotonic sequence guard in the reducer — never a shallow "latest wins". Independent panels fetch in parallel on mount (no waterfall await) and each holds its own load / success / error state so one failure never blocks the others; retry re-fetches only the failed endpoint. Long-lived polling is a chained setTimeout inside a useEffect — one outstanding request at a time, cleared on unmount — with exponential backoff on consecutive failures up to a ceiling and a reset on success; a banner surfaces after a failure threshold and clears on recovery. Local persistence via AsyncStorage debounces writes and rehydrates on mount BEFORE the network fetch resolves, and a POST submit clears the stored draft only on success. All HTTP is injected via a fetchImpl prop so tests bypass the network entirely.
Candidate Skill Search
The talent pool search a recruiter uses to shortlist candidates against a live vacancy. A controlled TextInput debounces requests to /api/candidates?q=, shouldSearch gates against the minimum query length, buildSearchUrl encodes and appends filter params, and each fired request carries a monotonically-increasing seq — searchReducer discards any success or error whose seq is older than the current one (stale-response guard). AbortController cancels the previous in-flight request on every new fire, and filter changes trigger exactly one further request with the current query. Loading / hint / error+retry / loaded (with empty variant) render one at a time.
Account Overview Panels
The overview screen of a retail banking app that loads several independent panels at once. On mount, App dispatches load for every panel in PANELS and fires fetchImpl for each in parallel — no await between them. Each panel is held in its own panelReducer slot so one failing endpoint renders panel-error-<key> + a retry Pressable while the other two render loaded. Retry re-fetches only that endpoint. The header total is derived on every render via sumAvailable(state) — never mirrored into state — and recomputes as balances resolves. buildEndpointUrl joins base + endpoint without duplicate slashes; the base URL is injected via the apiUrl prop.
Mortgage Application Progress
The screen an applicant watches while a submitted mortgage application is assessed. On mount StatusScreen fires the initial request and chains subsequent polls via setTimeout — one outstanding request at a time. On success dispatch payload, reset consecutiveFailures to 0, clear the banner, schedule the next poll at baseIntervalMs (or stop when isDecided). On failure increment consecutiveFailures, set the banner at failureThreshold, and schedule the next poll at nextIntervalMs(consecutiveFailures, baseIntervalMs, ceilingIntervalMs) — exponential backoff capped at the ceiling, reset on any success. Unmount clears the scheduled timeout so no further requests fire; pressing Stop polling halts further requests immediately. stageSummary derives the three-stage view (creditCheck / valuation / underwriting) from the payload with 'pending' defaults for missing keys.
Restaurant Stock Count
The stocktake screen a restaurant supervisor completes at close. Expected levels load from buildExpectedUrl(siteId); counts typed by the supervisor are written to AsyncStorage under draftKeyFor(siteId, countDate) — debounced so rapid typing collapses to one storage.setItem call per pause — and mergeDraftOverExpected rehydrates the saved draft on mount BEFORE the fetch resolves (restored-draft-notice surfaces only when a draft was found). computeVariance and computeTotals derive per-line variance and the { over, under, missing } totals on every render — never mirrored into state. A storage-area tab bar filters visible lines. Submit POSTs the counts via buildSubmitUrl(siteId); on success calls storage.removeItem(draftKeyFor(...)) and renders submit-success; on failure renders submit-error and keeps the draft on device. Star problem — accumulates the debounce + AbortController + stale-response discipline of 10286, the parallel-independent-state topic of 10287 and the polling-with-backoff topic of 10289, and lands them on offline-durable local persistence + a POST submit.
Persistance & Offline Storage
The device-persistence layer of a React Native app. Preferences, drafts and cached responses live in AsyncStorage under versioned or prefixed keys, and are always rehydrated on mount BEFORE the network is consulted so the UI shows the stored state instantly. Writes are debounced so rapid typing collapses to one setItem call per pause; a stored payload from an earlier version is migrated to the current shape rather than discarded; corrupt JSON falls back to defaults without throwing. Batch operations use multiSet, multiGet, multiRemove and getAllKeys with a prefix filter so aggregations across many stored records stay a single round trip. A cache-and-revalidate pattern serves cached data immediately and refetches behind the customer, respecting a TTL for skipping the request entirely and a hard staleness limit past which dependent actions are blocked.
Travel Search Preferences
A traveller's search preferences screen in a flight booking app. Cabin class, baggage requirement, party composition and preferred airports are persisted to AsyncStorage under a versioned key. On mount the app hydrates and renders defaults until hydration completes, migrating a stored v1 payload into the v2 shape rather than discarding it. A reset clears the key and returns to defaults; corrupt JSON falls back to defaults without throwing. A matching-fares summary derives from the current preferences.
Exchange Rate Board Cache
The rate board in a currency app that serves cached rates instantly and revalidates behind the customer. The fetched rate set is cached in AsyncStorage alongside a timestamp; on mount the cache renders immediately when present, then revalidates. Requests are skipped entirely inside the TTL; a "rates as at <time>" label shows until revalidation completes; the convert action is blocked past a hard staleness limit. Force-refresh always requests and rewrites the cache; a failed revalidation keeps the cached rates and leaves the staleness label showing. Data comes from the platform's hosted read-only JSON API over fetch with the base URL injected through configuration.
Vehicle Inspection Records
A fleet inspector's screen for recording defect grades across a depot with no connection. One record per vehicle is stored under a prefixed AsyncStorage key. Batch writes and reads use multiSet and multiGet; getAllKeys with a prefix filter lists them and ignores unrelated keys. A depot summary aggregates defect grades across all stored records; clearing a vehicle's record uses multiRemove and updates the summary. A rejected batch leaves no half-written record behind.
Storefront Category Sorter
A merchandising screen where storefront categories are dragged into display order that survives a restart. Categories are reordered with PanResponder, animating position through the Animated API and springing into the settled slot on release. The resulting order is persisted to AsyncStorage after each drop and rehydrated on mount. A category released outside any slot springs back to its origin and persists nothing. A reset-order control returns to the default order. Star problem — accumulates the AsyncStorage persistence + migration discipline of 10300, the cache-and-revalidate rhythm of 10301, and the batch storage + summary aggregation of 10302, and lands them on a drag-driven ordering interaction whose result must survive a remount.
Animation & Gestures
The motion layer of a React Native app. A single Animated.Value drives a bar width and a colour interpolation from amber to green; Animated.timing, Animated.spring, Animated.stagger, Animated.sequence and Animated.parallel compose the transitions between states. A reducedMotion flag skips the animation and sets the final value immediately so accessibility preferences are honoured. Gestures come from PanResponder — a drag moves a marker within clamped bounds and converts the offset into a live readout; release snaps to the nearest legal target with Animated.spring; a double press returns to a recommended value. Every running composite is stoppable so a skip control jumps straight to the final state; composites are torn down on unmount so no post-unmount state update fires.
Savings Goal Progress Bar
A savings goal progress indicator in a personal finance app. A single Animated.Value drives the bar width and a colour ramp from amber to green as the goal is approached, both derived from the same interpolated value. Logged contributions animate to the new position with Animated.timing; withdrawals animate back down. A reducedMotion prop skips the animation and sets the value immediately with no intermediate frames. The colour interpolation is not driven natively so the ramp keeps working under all render modes.
Fare Comparison Reveal
The results reveal on a fare comparison screen. Fare options reveal with Animated.stagger over per-row values; the best-value badge runs an Animated.sequence (scale up, then settle); dearer rows fade together with Animated.parallel. A skip control stops every running animation and jumps every value to its final state. Animations are stopped on unmount so no post-unmount state update fires.
Cinema Seat Picker
The seat picker a customer drags across the auditorium plan to choose where to sit. A PanResponder moves a marker within bounds, clamping at the edges and converting the offset into section / row / seat number shown live. Release snaps to the nearest available seat with Animated.spring — never onto a taken seat; a double press returns to the recommended seat. The responder is granted only on the plan and releases cleanly on gesture end.
Trip Itinerary Reorder
The day planner where a traveller long-presses a stop and drags it to a new position, with the reordered plan living in shared app state. A long press lifts a stop card and PanResponder translates it with the finger while the stops it passes animate aside with Animated.timing to open a gap. The plan is held in a context backed by a reducer; release dispatches a move with the source and target index, and a rejected move (a stop with a fixed booking time cannot cross another fixed stop) springs the card back with no state change. Neighbours translate only while a card is lifted and reset on release; consumers of unaffected days do not re-render; an undo control restores the previous order. Star problem — accumulates the Animated interpolation + reduced-motion of 10314, the Animated composition + cleanup of 10315, and the PanResponder + spring-snap discipline of 10316, and lands them on a shared reducer-backed plan with undo.
App State & Context
The shared-state layer of a React Native app. State lives in a useReducer, exposed through split contexts — a value context for the state itself and a dispatch context for the raw dispatch function, whose identity is stable across renders so callbacks passed into memoised children never change. When one part of the state changes at a different cadence to another — a ticking clock, a search query, a shortlist — it lives in its own third context so subscribers to unrelated slices don't re-render. Derived values (totals, filtered lists, grouped sections, counts) are computed by selectors on every render, never mirrored into a second useState. Row components subscribe to only the slice they render — often through a bins-by-id map so one bin's change gives that entry a new reference while every other entry stays === identical.
Travel Booking Basket
The basket a traveller taps through while assembling a trip from flights, rooms and extras. A basket context backed by a reducer handles add item, change quantity, apply option (fare class, board type) and remove line; taxes, fees and totals are computed by selectors rather than stored. Removing a line requires a reason recorded against it — an empty reason returns the previous state unchanged. The product grid does not re-render when only the basket changes
Store Rota Board
The rota board in a retail store manager's app where a ticking clock does not re-render the whole board. App state is split into separate value and dispatch contexts; the trading-hour clock lives in a third context of its own so shift rows subscribe only to what they use. Memoised consumers ensure a clock tick re-renders the clock strip alone; dispatching a shift assignment re-renders the affected row and nothing else. An interval ticks the clock every second and is torn down on unmount.
Property Search Board
The search board in a property app where typing in the search bar does not re-render every listing on screen. App state is split into value and dispatch contexts, and the search query lives in a third context of its own so listing rows subscribe only to the listing they render. Callback identities taken from the dispatch context stay stable across parent re-renders, so a keystroke re-renders the query bar and the result-count header alone. Data comes from the platform's hosted read-only JSON API over fetch, with the base URL injected through configuration so tests can point at a seeded instance.
Warehouse Bin Stock Console
The stock console where a large bin grid reads shared state without the whole grid re-rendering on every adjustment. Bin state lives in a context backed by a reducer with bins keyed by id; the grid renders every bin through a memoised row subscribing to its own bin only, with stable callback identities taken from the dispatch context. Adjusting one bin's on-hand quantity updates the totals bar without touching unrelated rows; a bulk cycle-count action gives each affected bin one new reference and leaves the rest unchanged. Star problem — accumulates the reducer + derived totals discipline of 10322, the split value / dispatch contexts of 10323, and the stable-callback / row-scoped subscription discipline of 10324, and lands them on a bins-by-id map that lets one change ripple through exactly one row.
Render Performance & Large Lists
The virtualisation and scheduling layer of a Hard-level React Native app. Long lists render through FlatList with a fixed row height and a getItemLayout derived from that so scrollToIndex lands exactly and no measurement pass is needed. Rows are wrapped in React.memo with a custom comparator that inspects only the fields the row actually renders — a change to any other field on the item never re-renders the row. Sort and filter results are memoised so scrolling never recomputes them. Heavy aggregation waits for the navigation transition via InteractionManager.runAfterInteractions, runs in chunks that yield to the event loop, reports progress after every slice, and cancels cleanly via AbortController when parameters change or the screen leaves — no post-unmount state update ever fires. Image-heavy grids use numColumns + removeClippedSubviews and hold viewabilityConfig frozen at module scope with a useRef-backed onViewableItemsChanged whose identity never changes. Where connectivity is intermittent, outcomes are queued to AsyncStorage and drained in order with idempotency (an already-synced entry is never posted twice).
Card Settlement Ledger
The settlement ledger in a merchant payments app holding a quarter of transactions that stays smooth while the merchant scrolls, sorts and adjusts. FlatList virtualises with getItemLayout and a fixed row height; memoised rows use a custom comparator examining only the fields they render (id, merchant, brand, last4, net, status, onOpen); sort and filter results are memoised so scrolling never recomputes them. Adjusting a line replaces only that row's reference. Data comes from the platform's hosted read-only JSON API over fetch reading orders, with the base URL injected through configuration.
Claims Loss Ratio Report
The loss ratio report in an insurance portfolio app aggregating a large claims dataset without blocking the screen transition or the touch response. Aggregation is deferred until after the navigation transition with InteractionManager.runAfterInteractions, chunked so each slice yields to the event loop, and rendered as a progressive skeleton that fills in as portions complete. Outstanding chunks cancel via AbortController when the parameters change or the screen is left; leaving cancels the remainder and produces no post-unmount state update. The final ratios equal a direct computation over the fixture.
Product Imagery Library
The imagery library a merchandiser browses, rendering hundreds of bundled product shots without exhausting memory. A FlatList grid uses numColumns and removeClippedSubviews, showing a placeholder that swaps to the image only once the item becomes viewable via onViewableItemsChanged held in a useRef so its identity never changes across re-renders. viewabilityConfig is frozen at module scope; rows are memoised so a scroll direction change does not remount them; images use fixed dimensions and an explicit resizeMode. Once revealed, a tile stays revealed even after scrolling out.
Delivery Round Board
The round board where a courier records hundreds of drops with no connection and the outcomes sync on return to depot. A virtualised drop list with memoised rows and stable callbacks records outcomes to local state and appends to a queue in AsyncStorage. A drain routine posts queued outcomes in order when an injectable connectivity stub reports a connection, marking each synced. A mid-drain failure leaves the remainder queued and retryable; an already-synced outcome is never posted twice; leaving the screen cancels the in-flight drain cleanly. Star problem — accumulates the FlatList virtualisation + custom-comparator memoisation of 10333, the cancellation + no-post-unmount-state-update contract of 10334, and the stable viewability / callback-identity discipline of 10336, and lands them on an offline queue backed by AsyncStorage.
About this path
This path starts with core components and styling and goes on to forms, lists, navigation, data fetching, offline storage, animation and gestures. You should know modern JavaScript, and some React experience helps, since React Native uses the same component model. No earlier mobile development experience is needed. Later modules focus on keeping large lists fast.
Frequently asked questions
4>What will I practice in this path?
12 modules: Core Components & Styling, Touchables & Press Interaction, State & Component Composition, TextInput & Forms, Platform & Responsive Layout, Advanced Lists & Virtualisation, Navigation with React Navigation, Data Fetching & Async State, Persistance & Offline Storage, Animation & Gestures, App State & Context and Render Performance & Large Lists.
>How many challenges are there, and how hard are they?
47 challenges: 20 easy, 23 medium and 4 hard.
>What does this path run on?
Challenges run on React Native 0.81.
>Do I need to install anything?
No. Every challenge runs in a browser-based IDE with everything already set up, and your code is graded automatically against a test suite.
