Best Practices
This final lesson doesn't introduce anything new — it pulls together the habits, from every earlier lesson in this track, that separate React Native code that merely "works" from an app that stays fast, maintainable, and pleasant to build on a year from now. Treat it as a checklist to revisit on real projects, not just a one-time read.
1. Keep Components Small and Focused
A single giant screen component, with dozens of nested <View>s and inline logic
all in one return, is hard to read and hard to change safely — the same advice given in
this site's Flutter track applies just as much here. Extract logical pieces into their own small,
named components instead of one sprawling tree.
// Instead of one 200-line component, extract pieces:
export default function ProfileScreen({ user }) {
return (
<View>
<ProfileHeader user={user} />
<ProfileStats user={user} />
<ProfileActions user={user} />
</View>
);
}
- A component that reads like a short outline is a good sign; one that scrolls for pages is a warning sign.
- Small, named components are also easier to give individual tests, covered below in point 7.
2. Choose State Management to Match Your App's Size
As covered in State Management: Context API and State Management: Redux & Zustand, there isn't one universally "correct" state management approach — the right one depends on scale.
- useState — simple, local, single-component state. Don't reach for more than this until you actually need to.
- Context API — simple state shared across several components, that changes infrequently.
- Zustand / Redux Toolkit — larger, more complex apps with frequent updates, many consumers, or a need for predictable, testable state logic.
3. Always Use FlatList for Lists That Could Grow Large
As covered in Lists: FlatList & SectionList and
revisited in Performance Tips, never use
ScrollView combined with .map() for a list whose length isn't a small,
fixed number — FlatList's virtualization is what keeps long lists smooth.
- Default to
FlatListunless you're certain the list will always be tiny. - Revisit any plain
ScrollViewlist the moment its content becomes user-generated or unbounded.
4. Handle Loading, Error, and Empty States Explicitly
As covered in Async/Await & Error Handling, never assume the happy path — every async data fetch has at least three real outcomes, and each deserves its own deliberate UI.
{loading && <ActivityIndicator />}
{error && <Text>Something went wrong. </Text>}
{!loading && !error && data.length === 0 && <Text>No results yet.</Text>}
{!loading && !error && data.length > 0 && <ProductList data={data} />}
- Loading, error, and empty are each a distinct, deliberate UI state — never left to default to a blank screen.
5. Remember fetch() Doesn't Throw on a 404 or 500
As covered in Networking: Fetch & Axios,
fetch() only rejects on a genuine network failure — a 404 or a 500 response still
resolves normally. Always check response.ok (or the status code) yourself before
trusting the body.
const response = await fetch('https://api.example.com/users');
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
const data = await response.json();
- Check
response.okbefore parsing the body, on every plainfetch()call.
6. Never Store Sensitive Data in Plain AsyncStorage
As covered in Local Storage: AsyncStorage,
AsyncStorage is unencrypted on-device storage — fine for preferences and cached lists,
but not appropriate for auth tokens, passwords, or other sensitive values. Use a secure storage
option (backed by iOS Keychain / Android Keystore) for anything sensitive instead.
- Preferences, cached lists, UI state — plain
AsyncStorageis fine. - Auth tokens, credentials, anything sensitive — use a secure storage library instead, never plain
AsyncStorage.
7. Write at Least a Few Component Tests for Your Critical Screens
As covered in Testing, you don't need full coverage everywhere to get real value — but the screens that would genuinely hurt if they silently broke (login, checkout, the core flow of your app) deserve at least a few tests each, written with React Native Testing Library and focused on what the user actually sees and does, not internal implementation details.
- Start with the flows that would be a real incident if broken, not exhaustive coverage everywhere.
- Query by visible text/role and simulate real presses — test behavior, not internals.
8. Back Up Your Android Signing Keystore Securely
As emphasized in Building & Releasing, losing your keystore means permanently losing the ability to update your published app under its existing Play Store listing.
- Store the keystore file and its passwords in at least two independent, durable places.
- Never commit a keystore or its passwords to a public repository.
9. Profile Before Optimizing
As covered in Performance Tips, use Flipper's Performance monitor or the React DevTools Profiler tab to find where re-renders and dropped frames are actually coming from before changing any code in the name of "performance."
- A change made without measuring first is a guess — sometimes it even makes things worse.
10. Lean on the Package Ecosystem, But Check What You Depend On
As covered in Native Modules & Device APIs, most common device features — camera, location, secure storage, push notifications — already have a solid Expo module or community package. Writing a custom native module yourself should be a last resort, not a first instinct. Before adding a package, though, glance at its npm download trend, its last publish date, and its open issues — a package that hasn't been touched in years, or has no maintainer response to open bugs, is a real risk to depend on long-term.
- Prefer an existing, well-maintained Expo module or community package over a hand-rolled native module.
- Check maintenance activity and popularity before adding a new dependency, not just whether it does the job today.
useState to Redux and Zustand, real HTTP APIs, native device access, animations, and
the practical work of debugging, testing, building, and releasing an app people can actually
install. That mental model, more than any single snippet, is the real payoff — and it carries
directly into any real React Native codebase you touch next. The "Next" button below leads back to
the Cross-Platform Apps hub, completing
every track offered there — Flutter, Cordova, and now React Native. Congratulations, and thank you
for following it all the way through.