| name | state-and-data |
| description | Review React Native data and state handling — server state with a query library, caching and staleness, offline behaviour, network transitions, transactions, and loading/empty/error states. Use when wiring up APIs, handling offline, or reviewing data flow. |
| version | 1.0.1 |
| platforms | ["ios","android"] |
| react-native-version | 0.76+ |
| tags | ["react-native","state","data","networking","offline"] |
State and Data Skill
Applicability
- Platforms: iOS and Android
- React Native: 0.76+ (New Architecture interop assumed unless a checklist item says otherwise)
When to Use
- Wiring a screen up to an API
- Reviewing how server state is fetched, cached, and displayed
- Handling offline mode, network transitions, or flaky connections
- Implementing payments, checkout, or other critical transactions
Guidance
Server State
Incorrect:
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/api/users').then(r => r.json()).then(setUsers);
}, []);
Correct:
const { data: users } = useQuery({
queryKey: ['users'],
queryFn: () => api.getUsers(),
staleTime: 5 * 60 * 1000,
});
Form & Local State
Offline & Network Transitions
Incorrect:
const data = await fetch('/api/items');
Correct:
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000);
try {
const data = await fetch('/api/items', { signal: controller.signal });
} catch (e) {
if (e.name === 'AbortError') showTimeout();
else showError(e);
} finally {
clearTimeout(timeout);
}
Transactions & Critical Actions
Incorrect:
const onPay = async () => {
await paymentSDK.charge(amount);
navigation.navigate('Success');
};
Correct:
const onPay = async () => {
const sdkResult = await paymentSDK.charge(amount);
const confirmed = await api.confirmPayment(sdkResult.transactionId);
if (confirmed.status === 'success') {
navigation.navigate('Success');
} else {
navigation.navigate('PaymentFailed', { reason: confirmed.reason });
}
};
Anti-Patterns
| Anti-Pattern | Risk | Fix |
|---|
useState + useEffect for API data | Race conditions, no cache, no retry | Use a query library |
| Global store for server data | Stale data, manual refetching | Let the query library own it |
| Direct fetch/axios in components | Untestable, no caching, repeated code | Abstract into query hooks |
| Fire-and-forget async | Silent failures | Always handle errors |
| Auto-retry POST on reconnect | Duplicate transactions | Retry only idempotent reads |
Pitfalls
staleTime: 0 (the default in many libraries) refetches on every screen focus, wasting bandwidth and battery.
- Optimistic updates without rollback on error leave the UI in an impossible state.
- Not cleaning up an
AbortController on unmount triggers a state update on an unmounted component.
- Showing stale cached data without an offline indicator makes users think it is current.
- Not storing transaction intent before starting means an app crash equals lost state.