When a feed of dynamic data has no RefreshControl, users have no way to force fresh content — they back out of the screen and re-enter just to trigger a fetch, or they assume the app is broken. A stuck refreshing={true} that never resets is worse: the spinner burns indefinitely, users wait, then force-quit. Both patterns inflate perceived load time and trigger abandonment before the real content loads.
Low because the app still functions, but the friction drives repeat navigation and degrades perceived freshness.
Add RefreshControl to every FlatList or ScrollView rendering dynamic data, and reset the refreshing flag in a finally block so network errors do not strand the spinner:
const [refreshing, setRefreshing] = useState(false);
const onRefresh = async () => {
setRefreshing(true);
try { await fetchData(); } finally { setRefreshing(false); }
};
<FlatList refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />} />
ID: mobile-ux-patterns.touch-gestures.pull-to-refresh
Severity: low
What to look for: Count all scrollable lists that display dynamic data (FlatList, SectionList, ScrollView with fetched content). For each, check whether RefreshControl is configured with an onRefresh handler and refreshing state.
Pass criteria: At least 100% of scrollable lists with dynamic data have RefreshControl configured with both refreshing state and onRefresh handler. Pull-to-refresh provides clear visual feedback (spinner, progress) and completes the refresh action without UI freeze. Report the ratio: "X of Y scrollable lists have RefreshControl."
Fail criteria: Any scrollable list with dynamic data is missing RefreshControl, or it exists but has janky animations or the refreshing state is never reset (spins indefinitely).
Skip (N/A) when: The app has no scrollable lists with dynamic data — all content is static or the app has no list views.
Detail on fail: "Pull-to-refresh missing from main feed — users cannot refresh without leaving and returning to the screen." or "RefreshControl exists but spins indefinitely after release."
Remediation: Add RefreshControl to ScrollView or FlatList with clear feedback:
const [refreshing, setRefreshing] = useState(false);
const onRefresh = async () => {
setRefreshing(true);
await fetchData();
setRefreshing(false);
};
<FlatList
data={items}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
renderItem={({ item }) => <ItemRow item={item} />}
/>