Keentune
Adaptive skill practice — find your level, get sharper.
React Native practice
React skills, applied to native mobile
How React translates to iOS and Android — rendering to real native views (not the DOM), flexbox layout and StyleSheet (with the column-default and density-independent quirks), FlatList virtualization and list performance, the JS-vs-UI thread split that governs smoothness, and the New Architecture (JSI, Fabric, TurboModules, Hermes, Reanimated worklets). Durable concepts first; version-specific architecture is flagged. Part of the Developer track.
• Meets you at your level — never too easy, never too hard.
• Adapts every question to how you’re doing.
• Tracks your level over time. Skill practice, not a test.
What you’ll master
Follow the curriculum from foundations through advanced practice. Each line is a concept the adaptive question bank can teach and test.
A. What React Native actually is
the UI is real platform widgets, not HTML rendered in a wrapped browser
a <View> becomes a UIView on iOS and an android.view.View on Android
no document, no window layout, no CSS files, no selectors, no HTML elements
a bare string outside a <Text> is a runtime error, not stray text
what ships in the react-native package versus what you install
your JavaScript runs on its own thread, not on the platform UI thread
shared logic is the promise; identical look and feel is not
a component prop sets a native view property, not an HTML attribute
the shipped bundle, its storage and its traffic are all inspectable, so secrets and enforcement belong on the server
B. Core components: View, Text, Image and friends
the container for layout, style, touch handling and accessibility
style inheritance exists only from a <Text> to a nested <Text>
truncation is the numberOfLines prop plus ellipsizeMode, not a CSS rule
a remote <Image> with no width/height lays out at zero
require() is resolved by the bundler, so a computed path cannot work
a bundled asset arrives through require() and a remote one through source={{ uri }}, and neither is an HTML src attribute
core ships TextInput, Button and Switch and nothing like the HTML input zoo, so a checkbox, a slider or a picker comes from elsewhere
cover, contain, stretch, center and repeat, and what each does to aspect ratio
layering children over an image without absolute positioning
a Modal renders in a native overlay above the whole app
several mounted StatusBar components merge, and the last one wins
expo-image's cache, blurhash stand-ins and load transitions, and why a list of remote images needs all three
react-native-svg and icon sets stay sharp at any density and recolour from a prop, where a raster asset does neither
video and audio through expo-video/expo-av, and the CPU and battery they cost when used as decoration
a sound or video holds a native player, so every load needs a matching unload in cleanup or it keeps playing after the screen is gone
play, pause, seek, rate, volume and loop are async METHODS on the loaded player rather than props on a component
playback state arrives through a status callback, where didJustFinish and isBuffering are how the end and a stall are observed
the audio session decides silent-switch behaviour, background playback, mixing, and what happens when a call interrupts
expo-av splitting into expo-audio and expo-video, a Video component versus an HTML tag, and streaming versus a bundled asset
page JS and app JS are separate contexts joined only by postMessage, so the injected surface is attack surface
embedding a hosted page or third-party widget instead of rebuilding it, and what that costs in performance and platform feel
source={{uri}} versus source={{html}}, and that the component ships in react-native-webview rather than in core
the embedded browser owns its own history and cookie jar, so URL changes, the back gesture and login state are its state and not the app's
C. TextInput and text entry
value without onChangeText freezes the field instead of just seeding it
numeric, number-pad, decimal-pad and email-address, and their platform parity
masking, and the multiline and autofill behavior it disables
auto-height, padding, and Android's textAlignVertical
onSubmitEditing fires on the return key, onEndEditing on commit
the default capitalization and autocorrect that mangle usernames
focus and blur are ref methods; there is no autoFocus-by-state prop
keyboardWillShow is iOS-only; Android only emits the Did events
there is no <form>, no HTML input types and no browser validation, so field state, errors and submission are all code you write
maxLength, editable, clearButtonMode, autoComplete and textContentType, including which of them are one platform only
every keystroke crosses to JS and back, which is why fast typing can drop characters
D. Touch, Pressable and the responder system
there is no click event; touch handling comes from wrapping in a touchable
Pressable exposes pressed through a style or children function
enlarging the touch target beyond the drawn bounds without changing layout
Opacity, Highlight, WithoutFeedback and Android's native ripple
ShouldSetResponder, Grant, Move, Release, Terminate
exactly one view owns a touch, and a parent can capture it
building a gesture recognizer on the responder system, in JavaScript
keyboardShouldPersistTaps decides whether the first tap only dismisses the keyboard
react-native-gesture-handler recognizes on the UI thread, unlike PanResponder
a gesture attaches through GestureDetector, and nothing works at all until the tree is wrapped in GestureHandlerRootView, which fails silently rather than erroring
Pan, Tap, LongPress, Pinch and Rotation each activate on their own condition and report their own fields, and the builder API is where they are configured
Simultaneous, Exclusive and Race, plus the external-gesture relations, decide which of two competing gestures is allowed to activate
onBegin, onUpdate, onEnd and onFinalize run on the UI thread, so reaching React state or navigation from them needs runOnJS
none, box-none, box-only and auto, and how they differ from the CSS property
E. StyleSheet and the style system
camelCased JavaScript objects, with no cascade, no selectors and no media queries
what registering a style buys you over a plain object literal
later entries override earlier ones, and falsy entries are skipped
a color set on a View does not reach the Text inside it
margin: '10px 20px' is invalid; each edge is its own numeric key
an unitless number is density-independent pixels, never CSS px
percentage strings work for size, margin and padding, not for every property
iOS shadow* props versus Android elevation, and what each ignores
transform is an ordered array; rotating then translating differs from the reverse
PlatformColor and DynamicColorIOS reference real system colors instead of hex
centralizing colors and spacing in one source so a change lands everywhere and themes are possible at all
CSS-in-JS, utility classes and compiler-based systems all reduce to RN style objects; what each costs at runtime and in ergonomics
there is no :hover or :active, so interactive state comes from component state such as Pressable's pressed
useColorScheme and the Appearance API report the system light/dark setting, and a real theme follows it by default while letting the user override and persist a choice
F. Yoga layout, and where it diverges from web CSS
there is no display: block; every view is already a flex container
flexDirection defaults to column in RN and to row on the web
the web default is 1, which is why a fixed badge overflows a row instead of shrinking
flex: 1 expands to grow 1, shrink 1, basis 0 percent
distribution along the main axis, which follows flexDirection
children stretch across the cross axis unless given a size
one child overriding the container's cross-axis alignment
flexWrap defaults to nowrap, so children compress rather than move to a new line
absolute positioning is relative to the parent, and there is no page to position against
no position: fixed, because there is no scrolling document viewport
no float, no CSS grid; flexbox plus absolute positioning is the whole toolkit
gap, rowGap and columnGap are real style properties in modern React Native, so even spacing no longer needs a margin on every child but one
overflow defaults to visible, and on Android elevation competes with zIndex
Yoga's aspectRatio sizes one dimension from the other
measured size arrives in a callback after layout; you cannot read it during render
layout is computed in C++ on the shadow thread, not in your JavaScript
G. Units, density and responsive dimensions
a style number is a logical point; the device multiplies it by its density
PixelRatio.get() and getPixelSizeForLayoutSize for asset and hairline math
the user's accessibility text-size setting scales fonts independently of screen density
the hook re-renders on rotation; Dimensions.get() captures a stale value
on Android the window excludes the system bars and the screen does not
StyleSheet.hairlineWidth is the thinnest line the current device can draw
notches, status bars and home indicators define where content may not sit
Android 15 draws behind the system bars by default, so insets must be consumed
H. Lists and virtualization
FlatList unmounts off-screen rows; a ScrollView mounts every child forever
a stable identity per row; falling back to the index breaks reorder and deletion
the callback receives { item, index, separators }, not the item alone
padding belongs on the content container; style sizes the scroll frame
supplying row geometry skips measurement and makes scrollToIndex work immediately
first-paint cost traded against blank space on an immediate fast scroll
how many viewports of content stay mounted around the visible one
rows per render pass: throughput against dropped frames
the memory win, and the blank-cell artifacts it can cause
blank space means JS could not render rows as fast as the user scrolled
an inline arrow renderItem re-renders every row; memoized rows need extraData
grouped data with section headers, and sticky-header behavior
the threshold is measured in list lengths, and it can fire twice
ListHeaderComponent, ListFooterComponent, ListEmptyComponent and ItemSeparatorComponent are the list's own slots, not children
horizontal and numColumns change the list's axis and grid, and columnWrapperStyle styles each generated row
I. ScrollView, keyboard and safe area
the cost model, and the list sizes where it is still the right choice
a ScrollView with an unbounded parent grows instead of scrolling
dismissing the keyboard on drag, and its interaction with taps
how often onScroll fires on iOS, and the default that fires once
padding, height and position behaviors, and why the right one differs per platform
the core component is a no-op on Android, which is why the insets hook replaced it
pull-to-refresh is a prop of the scrollable, not a screen-level wrapper
J. Platform differences and platform-specific code
Platform.OS is ios, android, windows, macos or web under React Native Web
per-platform values inline, with a default key as the fallback
the bundler picks .ios.tsx or .android.tsx with no import change
.native.js splits native from web in a shared codebase
a number (the API level) on Android, a string on iOS
only Android has a system Back gesture, and a handler must return true to consume it
Material ripple feedback versus the iOS fade, and how to get each
system fonts differ, so a fontFamily string valid on one platform silently fails on the other
default font, line height and vertical centering are not identical across platforms
Android prompts at runtime, iOS prompts at first use with a declared purpose string
K. Animation
a mutable value driven outside React state so no re-render is needed per frame
only Animated.View and friends, or createAnimatedComponent, accept an animated style
the three drivers, and the motion each models
the animation is handed to the UI thread and keeps running while JS is busy
the native driver covers non-layout properties such as transform and opacity, not width or top
mapping one driver's range onto colors, degrees, or any output range
sequence, parallel, stagger and loop
creating an Animated.Value during render resets it every pass
declarative animation of the next layout pass, with an Android opt-in flag
it applies to every layout change in that pass, not to one component
animation logic compiled to run on the UI thread with shared values
L. The New Architecture: Fabric, TurboModules, JSI, bridgeless
the old bridge serialized every call to JSON and delivered it asynchronously in batches
a chatty or large payload queued behind other traffic and dropped frames
a C++ interface that lets JavaScript hold and call native objects directly
a native object exposed to JS with no serialization step
native modules initialize on first use instead of all at startup
native interfaces generated from a typed JS spec so both sides cannot drift
the C++ renderer that replaced the platform-specific UIManager
an update clones the affected nodes rather than mutating them
the three phases of the Fabric pipeline, and what happens in each
a commit lands in one frame, so no torn intermediate state is visible
Fabric can measure and lay out without an async round trip to JS
JS, UI and background threads, and what work is allowed on each
the bridge object is gone entirely; modules are reached through JSI
the renderer collapses layout-only views out of the native hierarchy
Fabric, Yoga, JSI and the TurboModule infrastructure live in one C++ core every platform links against, instead of a port maintained per platform
M. Hermes and the JavaScript environment
Hermes ships as the default engine on both platforms
the bundle is precompiled at build time, so no parsing happens at launch
the payoff is time-to-interactive and resident memory, not raw throughput
without a JIT, a long hot numeric loop can run slower than on JSC
the concurrent generational collector, and the pause behavior it targets
an over-the-air bundle must match the installed engine's bytecode version
no DOM, but polyfilled fetch, timers, console and URL
internationalization support is optional because it grows the binary
the engine speaks the Chrome DevTools Protocol directly
N. Native modules and native components
reaching an OS capability that has no JavaScript binding
the TypeScript or Flow spec file, not the implementation, defines the interface
build-time generation of the argument parsing on both sides
only a fixed set of types crosses; arbitrary class instances do not
most module methods return a promise, and a synchronous method has real constraints
emitting events to JS, and honoring add/remove listener counts
exposing a platform view as a React component with typed props
UI work must be dispatched to the platform main thread or it crashes
native dependencies are wired into Xcode and Gradle without manual edits
a Swift and Kotlin DSL as an alternative to hand-writing Codegen specs
O. Performance
roughly 16 ms per frame at 60 Hz, and both threads must meet it
a stalled JS thread freezes touches; a stalled UI thread freezes scrolling
reading the two separate frame-rate counters on device
dev-mode assertions and warnings make debug timings meaningless
logging in a per-frame path is a measurable cost, especially when a debugger is attached
an avoidable re-render is more expensive here because it can produce native mutations
memory and CPU scale with the source resolution, not the displayed size
non-rectangular shadows are expensive to render on Android
deferring module evaluation until first use shrinks startup work
deferring expensive work until animations and gestures have finished
bundle load, module initialization and first render are separate, separately fixable costs
P. Debugging and developer tooling
React Native DevTools attaches to the running engine over the Chrome DevTools Protocol
the old remote JS debugging ran your code in a browser engine, so behavior differed
component edits preserve state; editing a non-component export forces a full reload
errors versus warnings on device, and why an ignored warning can hide a real bug
turning a minified release stack trace back into file and line
a stale transform cache serves old code; clearing it is the first move
how to open it on a device, a simulator and an emulator
logcat and the Xcode console catch native crashes that JS logging never sees
a class component catching render errors in its subtree, and the three places it cannot reach: event handlers, async code, and anything outside React rendering
try/catch and .catch for the failures a boundary never sees, surfaced as an error state rather than an unhandled rejection
reporters, breadcrumbs and uploaded source maps, and why a console log reaches nobody once the app has shipped
fallbacks that let the user recover, retry with backoff, and telling a recoverable failure from a fatal one
RNTL's query priority: role, label and text before testID, and getBy versus queryBy versus findBy
mocking native modules, AsyncStorage, navigation and fetch so a test runs in Node with no device
findBy and waitFor instead of fixed delays, and what an act warning is actually telling you
many unit tests, fewer integration, fewest Detox e2e, and why snapshots go stale rather than catching regressions
jest-expo plus React Native Testing Library render to a host-component tree rather than a DOM, so queries and assertions differ from web testing
a JS exception shows an error screen; a native exception kills the process
Q. Metro, bundling and assets
resolution, transformation and serialization into a single bundle
no route-level code splitting the way a web bundler does it
the resolver's module rules, and why monorepo symlinks need configuration
JSX and unsupported syntax are compiled for the engine before it ever runs
@2x and @3x files are chosen at load time by device density
teaching the bundler about a new source or asset file type
in development the app fetches from Metro; a release build embeds the bundle
R. Accessibility
accessible collapses a subtree into one focusable element
the text a screen reader announces instead of the visible children
button, link, header or image changes how the element is announced and acted on
disabled, selected, checked and busy belong in state, not in the label
describes the result of acting, and should not repeat the label
polite versus assertive announcement of content that changed without user action
querying at runtime whether a screen reader is enabled
respecting the OS animation preference instead of always animating
the minimum recommended tappable area, and hitSlop as the fix
removing purely visual elements from the accessibility tree
accessibilityIgnoresInvertColors keeps a photo recognizable when iOS Smart Invert is on
state and meaning need a second cue besides hue, and text needs enough contrast to survive low vision and sunlight
the order assistive tech moves through elements, and why it follows the view tree rather than visual position
VoiceOver and TalkBack differ in behaviour and prop support, so both are exercised by hand on device
accessibility is measured by whether people with diverse abilities can complete the task, not by passing a one-time audit
S. Navigation
there is no URL bar or history stack; routing is application code
push and pop, and the platform back gesture each stack provides
a screen you navigated away from is still mounted, so its timers keep running
route params should be small identifiers, not objects or callbacks
mapping an incoming URL onto a navigation state, including nested stacks
backing screens with native containers instead of plain views
routes derived from the filesystem, with layouts and route groups
a route presented modally by the navigator, which is not the <Modal> component
stack, tabs and drawer each answer a different structural question, and a real app nests them rather than choosing one
the header's title, back button and visibility are navigator options set per screen or per group, not a component you render
T. Device APIs: networking, storage, permissions and app state
fetch and XHR are polyfilled, and native requests are not subject to browser CORS
iOS App Transport Security and the Android network security config reject plain HTTP by default
a socket is supported, but the OS may suspend it when the app backgrounds
an unencrypted key-value store returning promises, not a synchronous object
tokens belong in the iOS Keychain or Android Keystore, never in AsyncStorage
the sandbox's persistent directory versus the disposable one the OS may evict under storage pressure, and what belongs in each
a file is addressed by a file:// URI, and every read, write, download and info call returns a promise
image, camera and document pickers hand back a URI and a cancelled result, and writing to the device gallery is a separate permission
uploading a local file by URI, shrinking it before it leaves the device, and deleting the temporary copy afterwards
active, background and the iOS-only inactive state, and what each means
opening an external URL, and the scheme declaration canOpenURL requires
getInitialURL handles a launch from a link; the event listener handles a warm one
requesting at runtime, and handling the never-ask-again result
a missing Info.plist purpose string crashes the app when the permission is requested
notifications, sensors, haptics and the clipboard come from native modules, not from the web APIs of the same name
a push token identifies one INSTALL, can change, and is useless until your server has stored it
foreground, background and quit each deliver differently, and who shows the notification changes with them
routing data travels in the payload, and the cold-start tap is the one most often left unhandled
scheduled on-device versus pushed from a server, plus Android channels, categories and badges
fetch rejects only on network failure, so an HTTP 404 or 500 resolves normally and the status must be checked by hand
loading, error, empty and success are four distinct states a screen has to render, and forgetting one is what strands a spinner
cache-then-revalidate, query keys, optimistic updates and invalidating after a mutation
aborting on unmount, discarding stale responses when two requests race, and debouncing input before it reaches the network
reading from a local cache first and replaying queued mutations once connectivity returns
the OS decides whether and when registered background work runs, so nothing the product needs can depend on it having run
timers, sockets, playback and location all have to be released, and unsaved state written, before the process can be suspended or killed
the foreground transition is where stale data, dropped sockets and expired sessions get repaired in one place
a duration survives backgrounding only when it is recomputed from a stored timestamp, because JS timers are throttled or stopped
one configured client with a baseURL, and interceptors as the single place auth headers, 401-refresh and error normalization belong
a request needs a deadline, and a retry needs bounded, increasing, jittered delays instead of a tight loop
asking for exactly the fields a screen needs, and what a normalized client cache buys over ad-hoc response storage
WebSocket, SSE and polling differ in direction, latency and what they cost a mobile radio
authenticating at the handshake, and rebuilding the subscription state a dropped mobile socket loses
U. Expo: modules, config and EAS
a prebuilt client with a fixed native runtime versus your own compiled client
modifying the native projects declaratively from app config instead of editing them
the ios and android directories are build artifacts that can be regenerated
cloud builds with remotely managed signing credentials
an update only applies to a build whose runtime version matches
a JS update can never add a native dependency; that needs a new store build
one config file driving both platforms, and static versus dynamic config
Expo SDK modules can be installed in a plain React Native app
each SDK targets one React Native version, and mixing them breaks the build
the usePermissions pattern for status, request and re-check
channels, when a downloaded update actually applies, and rolling back without a store review
V. Shipping: builds, signing and upgrades
what changes between the two builds, from asserts to bundling to logging
the upload key is the app's identity; losing it blocks future updates
certificates, identifiers, profiles and device registration
the bundle identifier and application id are effectively immutable once published
the marketing version users see versus the build number stores require to increase
code shrinking breaks anything resolved by name at runtime
upgrading React Native means diffing the native template, not just bumping a dependency
uploading source maps and dSYMs so crash reports are readable
React Native core ships to iOS prebuilt, so a clean Xcode build stops recompiling the framework from source
W. React hooks, as they behave in a React Native app
hooks run at the top level in the same order every render, which is why a conditional hook breaks the mapping between call site and stored state
updates are batched and asynchronous, the functional setter exists for previous-state reads, and setting an identical value can bail out of the render
what the dependency array actually compares, and why the returned cleanup runs before every re-run as well as on unmount
an effect or callback captures the values present when it was created, so a missing dependency freezes an old one
a mutable box that survives renders without causing one, which is what makes it right for timers, subscriptions and component handles
memoizing a VALUE versus memoizing a function's IDENTITY, and when each is worth its own cost
reading shared state without threading props, and why a fast-changing context value re-renders every consumer
when structured transitions beat several independent useState calls
composing hooks into a reusable use-prefixed function, and why the naming convention is what the lint rules key on
X. Shared state: what to reach for, and what it costs
most state belongs in the component that uses it, and prop drilling is the specific problem that justifies sharing
Context suits values that change rarely, because every consumer re-renders on every change
Redux and RTK, Zustand, Jotai and MobX: what each buys, and the boilerplate or magic each costs
fine-grained subscription is the real reason a store beats Context for app-wide state
one authoritative location per value, and relational data keyed by id rather than duplicated
remote data needs caching, refetching and request state, which a client store does not provide
surviving a restart, and why nothing persists on its own
a toggle in the global store is the clearest sign state was lifted too far
Y. Mobile security: what the client can and cannot protect
a bundled key is extractable and cannot be revoked per install, so it belongs behind a proxy you control
TLS for everything, App Transport Security blocking cleartext, and where certificate pinning helps against a forged certificate
short-lived access tokens with refresh, the PKCE authorization-code flow on mobile, and re-authentication for high-risk actions
local authentication improves the experience, and the server still has to authorize
encrypting local data, screenshot and app-switcher flags, the shared clipboard, and what production logs capture
requesting only the permissions actually needed, and treating deep-link parameters as untrusted
layered controls, and why obfuscation raises the bar without being a control
a loaded page is untrusted code: origin allow-listing, the smallest possible injected surface, and validating every message it sends back
a public client cannot hold a secret, so a verifier it generates per flow is what makes an intercepted authorization code worthless
the login flow runs where the app cannot read the credentials and the user can see whose page it is, which also lets existing sign-in sessions apply
an exactly-registered custom-scheme or universal-link callback, and state tying the response back to the request that began it
an ID token says who the user is and an access token authorizes a call, so reading a profile out of the access token is the wrong tool
Z. Localization and right-to-left layout
user-facing text lives in per-language resource files referenced by key, and a string left inline is a string that can never be translated
a semantic key survives a wording change where the English sentence as a key does not, and a missing entry resolves to a fallback rather than rendering the key
placeholders and plural categories keep a sentence one translatable unit, because word order and plural rules both vary by language
dates, numbers and currency come from Intl for the active locale, never from a format string maintained by hand
reading the device locale at startup, persisting an explicit user choice above it, and applying a switch without a reinstall
translated text can be far longer than the source, and accented placeholder text surfaces the truncation before a translator is paid
I18nManager.isRTL puts the whole layout in a mirrored mode, forceRTL applies on the next launch, and direction-implying assets have to mirror with it
start/end follow the writing direction where left/right are frozen, so one hard-coded side is what leaves a layout half-mirrored
AA. Maps and device location
a single fix versus a subscription that must be removed, and why polling the one-shot call is the wrong shape for tracking
accuracy trades against battery and time-to-fix, and a distance or interval filter is what makes continuous tracking affordable
foreground and background location are separate grants, asked for in context, and a denial has to degrade rather than crash
geocoding both ways, heading as a signal distinct from position, and geofencing as region entry and exit
the viewport is a region of centre plus deltas, recentring means updating it or animating the camera through a ref, and region events fire far too often to fetch on
markers, callouts, custom marker children, polylines, and clustering once there are more points than the renderer should draw
which native map actually renders, and the API key and native config a blank Android map is reporting
AB. Typing a React Native app
a Props type on the component, React.ReactNode for children, () => void for callbacks, and ? for optional props
StyleProp<ViewStyle> accepts arrays and falsy entries where a bare ViewStyle does not, and a ref is typed to the COMPONENT
a ParamList plus the navigator's own screen-props helper is what makes route params type-safe end to end
useState<T | null>, an async function's Promise<T> return, and pairing useReducer's state with its action union
a literal discriminant narrows the payload, and a never assertion in the default case makes the switch exhaustive
a type parameter lets a list component or a fetch hook serve every caller without widening to any
any switches checking off, unknown forces narrowing, and as asserts without changing anything at runtime
what strict mode actually catches, and using typeof/keyof so a type cannot drift from the value it describes
Keentune is not affiliated with or endorsed by the organizations whose documentation informs these maps.
Explore all skills on Keentune
All exam, test, and product names and trademarks are the property of their respective owners and are used here for identification and reference only. Keentune is independent study practice — not affiliated with, authorized, or endorsed by any of these organizations.
© 2026 SportaApp LLC