Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
Windows Tipps & SecurityXbox x IKEA furniture range goes live today(01.10.2026 um 12:29 Uhr)
••
Windows Tipps & SecurityThe Witcher 3 Remastered's DLSS Xbox Play Anywhere issues has been fixed(01.10.2026 um 12:50 Uhr)
•••
Sicherheitslücken (CVE)CVE-2022-45425 | Dahua DHI-DSS4004-S2 hard-coded key (EUVD-2022-48297)(01.10.2026 um 12:29 Uhr)
•••••
Windows Tipps & SecurityXbox x IKEA furniture range goes live today(01.10.2026 um 12:29 Uhr)
••
Windows Tipps & SecurityThe Witcher 3 Remastered's DLSS Xbox Play Anywhere issues has been fixed(01.10.2026 um 12:50 Uhr)
•••
Sicherheitslücken (CVE)CVE-2022-45425 | Dahua DHI-DSS4004-S2 hard-coded key (EUVD-2022-48297)(01.10.2026 um 12:29 Uhr)
•••••
Intelligence View
⚡ tsecurity.de Intelligence

React Native Hooks in Depth — Examples, Scenarios & Production Patterns

React Native Hooks in Depth — Examples, Scenarios & Production Patterns Introduction You open a screen, fetch a list, tap a favorite, rotate the phone, go to b…

Beitrag
0
Seite
0
↗ Quelle (dev.to)
Social ReaktionenReagiere als Erste:r — dein Feedback zählt!




React Native Hooks in Depth — Examples, Scenarios & Production Patterns






Introduction



You open a screen, fetch a list, tap a favorite, rotate the phone, go to background, come back — and suddenly you have a memory leak, a double API call, or a button that feels laggy.



In modern React Native, almost all of that logic lives in hooks.



What are hooks?


Hooks are functions that let function components use state, side effects, refs, context, and memoization — without class components. Same React hooks work in React Native; the difference is where you use them (screens, lists, native modules, AppState, dimensions, safe areas).



Why this matters in React Native




  • Screens mount/unmount more often than web pages (navigation stacks)

  • Lists re-render aggressively (FlatList)

  • Device events (keyboard, AppState, orientation) create real side effects

  • Wrong useEffect / missing cleanup = leaks, duplicate network calls, jank



What this article covers




  1. Mental model & Rules of Hooks


  2. What hook to use and where (decision guide)

  3. Core hooks with scenario-based examples

  4. React Native–specific hooks

  5. Navigation hooks

  6. Custom hooks you will actually reuse

  7. Common pitfalls + checklist





Table of Contents





  1. Mental model


  2. Rules of Hooks


  3. What hook to use and where


  4. useState


  5. useEffect


  6. useLayoutEffect


  7. useRef


  8. useMemo & useCallback


  9. useReducer


  10. useContext


  11. RN platform hooks


  12. Navigation hooks


  13. Custom hooks


  14. Full screen example


  15. Pitfalls


  16. Checklist







Body





1. Mental model: what a hook really does











































Hook family Job RN example
State Remember UI data across renders Form input, modal open
Effect Sync with outside world API fetch, AppState, listeners
Ref Mutable value / native node Scroll position, TextInput focus
Context Share data without prop drilling Theme, auth user
Memoization Skip expensive work / stable fns List renderItem, heavy filters
Reducer Complex / multi-step state Checkout wizard, filters


Golden rule




Hooks run top-to-bottom on every render. Effects run after paint. Cleanup runs before next effect or on unmount.




Render vs commit (RN mental model)





  1. Render — your function runs; hooks return current values


  2. Commit — React applies updates to native views


  3. useLayoutEffect — runs after commit, before the user paints


  4. useEffect — runs after paint — safe for network, AppState, logging







2. Rules of Hooks (non-negotiable)




  1. Only call hooks at the top level (not inside if, loops, or nested functions)

  2. Only call hooks from React function components or custom hooks

  3. Custom hooks must start with use

  4. Dependency arrays must be honest — include values you read from the closure



// BAD — conditional hook (order can change between renders)
if (isLoggedIn) {
useEffect(() => {}, []);
}

// GOOD — condition inside the effect
useEffect(() => {
if (!isLoggedIn) return;
// ...
}, [isLoggedIn]);





Strict Mode note (React 18+ / modern RN): In development, React may mount → unmount → remount and run effects twice to surface missing cleanups. If you see double API calls only in dev, check cleanup first — don’t “fix” it by disabling Strict Mode.







3. What hook to use and where



Use this section as a decision map. Pick the situation on the left → use the hook on the right.





3.1 One-glance decision table
























































































You need to… Use Where it usually lives
Show / change UI data (input, toggle, modal, loading flag) useState Screen, component, custom hook
Many related updates / wizard / state machine useReducer Complex screens, form flows
Run something after render (API, listener, timer) useEffect Screens, providers, custom hooks
Fix layout/scroll before user sees a flash useLayoutEffect Lists, measuring views, chat scroll
Keep a value without re-render (timer id, flag) useRef Any component / custom hook
Call imperative native API (.focus(), scroll)
useRef + ref={}
Inputs, FlatList, video players
Share auth / theme / locale across many screens
useContext (+ Provider)

App.js / root → read in screens
Cache expensive derived data useMemo Filters, sorted lists, heavy calc
Keep a stable function for list / memo child useCallback
FlatList handlers, memoized rows
Reload when screen is focused (tabs) useFocusEffect Tab screens, stack screens that stay mounted
Navigate or read route params
useNavigation / useRoute
Screens inside a navigator
Pad for notch / home indicator useSafeAreaInsets Custom headers, full-screen modals
React to rotate / width breakpoints useWindowDimensions Responsive layouts, grids
Follow system light/dark useColorScheme Theme tokens, screen backgrounds
Reuse the same logic in 2+ places
Custom hook (useX)
src/hooks/




3.2 Decision flow (ask in order)





Does the UI need to update when this value changes?
YES → useState (or useReducer if transitions are complex)
NO → Is it a DOM/native node or “latest value” box?
YES → useRef
NO → Is it shared across many screens?
YES → useContext (or external store)
NO → Derive it: const x = ... or useMemo if expensive

Is this talking to the outside world (API, AppState, Keyboard)?
YES → useEffect (cleanup!)
Need it on every focus in a tab? → useFocusEffect
Need zero visual flash? → useLayoutEffect

Is a child / FlatList re-rendering too much because props change identity?
YES → useCallback for functions, useMemo for objects/arrays
NO → don’t add memo hooks yet







3.3 Where each hook belongs in an RN app











































Layer Typical hooks Avoid here
Root (App.js)
useState/useReducer for bootstrapping, Context Providers
Heavy fetch for every screen

Providers (Auth, Theme)

useState, useMemo (value), useEffect (hydrate token)
Navigation hooks (not inside nav yet)
Navigators Rarely — keep thin Business useEffect fetch
Screens
useState, useEffect / useFocusEffect, nav hooks, safe area, dimensions
Deep prop drilling — lift to context/hook
List rows / pure UI Prefer props only; maybe useState for local press/animation Fetching, AppState, navigation side effects
src/hooks/ Any composition of the above JSX (hooks return data/fns, not UI)




3.4 Situation → hook (scenario cheat sheet)












































































































Scenario Hook(s) Why
TextInput value useState UI must re-render as user types
Modal open/close
useState or useToggle
Simple boolean UI
Checkout steps + address + payment useReducer Multiple coordinated transitions
Fetch on first mount only useEffect([]) One-shot sync with server
Fetch every time user opens a tab useFocusEffect Tab screen often stays mounted
Search while typing
useState + useDebouncedValue + useEffect
Debounce then fetch
Pause video in background
useEffect + AppState (or useAppState)
Subscribe/cleanup native event
Focus password field useRef Imperative .focus(), no re-render
“Latest props” inside a long-lived listener
useRef + useEffect
Avoid re-binding listener every render
Filter 5k products useMemo Expensive derive; don’t store duplicate state

FlatList renderItem / onPress
useCallback Stable identity → fewer cell updates
Auth user in Profile + Home + Settings
useContext / useAuth
Avoid prop drilling through navigators
Custom header under notch useSafeAreaInsets Device-correct padding
2 columns phone / 3 tablet useWindowDimensions Responds to rotation
Dark mode colors useColorScheme Follows system appearance
Prevent leave with dirty form
useNavigation + useEffect (beforeRemove)
Navigation lifecycle
Same fetch logic on 3 screens
useFetch / React Query
Share logic; Query if you need cache
Cache + retry + focus refetch TanStack Query / SWR Don’t hand-roll this in useEffect
Cancel in-flight request on leave
AbortController in effect cleanup
Cleaner than isMounted flags




3.5 What not to use (common mis-picks)





















































Tempting choice Better choice Why

useState for items.length
Derive: items.length
Don’t sync derived data

useEffect to update state from props
Compute during render Avoid extra render loops

useEffect for tab refetch
useFocusEffect Mount ≠ focus

useMemo on every variable
Use only when measured Noise, little gain

useCallback on every function
Use for list/memo children Same reason

useRef for something shown in UI
useState Ref changes don’t re-render
Context for high-frequency values (scroll X)
useRef / Reanimated shared value
Context re-renders all consumers
Fetch inside list row Fetch in screen / query hook N rows = N requests




3.6 Mini map — “I am building X”



Login screen




  • Fields → useState

  • Submit loading/error → useState

  • Focus next input → useRef

  • On success navigate → useNavigation



Feed / Explore




  • Data/loading → useState or useFetch

  • Debounced search → custom useDebouncedValue

  • Refetch on focus → useFocusEffect

  • Grid columns → useWindowDimensions

  • Row press → useCallback + useNavigation



Chat




  • Messages → useState / useReducer

  • Scroll to end without flash → useLayoutEffect + useRef

  • Socket subscribe → useEffect + cleanup

  • App background → useAppState



Profile / Settings




  • User → useAuth (useContext)

  • Local form draft → useState

  • Unsaved guard → useNavigation listener

  • Safe header → useSafeAreaInsets



Root app shell




  • Providers → Auth/Theme with useState + useMemo

  • Bootstrap token → useEffect once

  • Not: per-screen list fetching







4. useState — UI memory





What it is



useState stores a value that survives re-renders and triggers a re-render when updated.




const [value, setValue] = useState(initialValue);
// Lazy init — function runs once on mount (good for expensive setup)
const [value, setValue] = useState(() => expensiveCreate());






Use when: the user (or UI) must see the change.


Don’t use when: the value is derived (items.length) or only needed imperatively (useRef).





Scenario: Favorite toggle on a product card



Problem: User taps heart; icon should flip instantly and stay in sync with server.




import React, {useState} from 'react';
import {Pressable, Text} from 'react-native';

function FavoriteButton({productId, initiallyFavorite}) {
const [isFavorite, setIsFavorite] = useState(initiallyFavorite);
const [loading, setLoading] = useState(false);

const onToggle = async () => {
if (loading) return;

const next = !isFavorite;
setIsFavorite(next); // optimistic
setLoading(true);

try {
await api.setFavorite(productId, next);
} catch {
setIsFavorite(!next); // rollback
} finally {
setLoading(false);
}
};

return (
<Pressable onPress={onToggle} disabled={loading}>
<Text>{isFavorite ? '♥' : '♡'}</Text>
</Pressable>
);
}






Why this pattern




  • Optimistic update feels native-fast

  • Rollback keeps UI honest when the network fails

  • Functional updates avoid stale state on rapid taps






Functional updates (avoid stale state)






// BAD if multiple updates queue quickly
setCount(count + 1);

// GOOD — always based on latest queued value
setCount(prev => prev + 1);









Scenario: Counter with rapid taps






import React, {useState} from 'react';
import {Pressable, Text, View} from 'react-native';

function CartQty() {
const [qty, setQty] = useState(1);

return (
<View style={{flexDirection: 'row', alignItems: 'center', gap: 12}}>
<Pressable onPress={() => setQty(q => Math.max(1, q - 1))}>
<Text>-</Text>
</Pressable>
<Text>{qty}</Text>
<Pressable onPress={() => setQty(q => q + 1)}>
<Text>+</Text>
</Pressable>
</View>
);
}












5. useEffect — side effects & cleanup






What it is



useEffect runs code after render to sync with something outside React: APIs, timers, event emitters, native listeners.






Dependency patterns
























Deps When it runs
[] Mount once (+ cleanup on unmount)
[a, b] Mount + whenever a or b changes
omitted Every render (almost always a bug)





Scenario: Fetch list when screen opens






import React, {useEffect, useState} from 'react';
import {ActivityIndicator, FlatList, Text} from 'react-native';

function ExploreScreen() {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
let alive = true;

(async () => {
try {
setLoading(true);
const data = await api.getExplore();
if (alive) setItems(data);
} catch (e) {
if (alive) setError(e.message);
} finally {
if (alive) setLoading(false);
}
})();

return () => {
alive = false; // prevent setState after unmount / navigation away
};
}, []);

if (loading) return <ActivityIndicator />;
if (error) return <Text>{error}</Text>;

return (
<FlatList
data={items}
keyExtractor={item => String(item.id)}
renderItem={({item}) => <Text>{item.title}</Text>}
/>
);
}






Why alive (or AbortController)?


User can leave the screen before the request finishes. Updating state after unmount wastes work and hides missing cleanups (especially under Strict Mode). Prefer aborting the request when you can:




useEffect(() => {
const controller = new AbortController();

(async () => {
try {
const data = await api.getExplore({signal: controller.signal});
setItems(data);
} catch (e) {
if (e.name === 'AbortError') return;
setError(e.message);
}
})();

return () => controller.abort();
}, []);









Scenario: Pause video when app goes to background






import {useEffect} from 'react';
import {AppState} from 'react-native';

function usePauseOnBackground(videoRef) {
useEffect(() => {
const sub = AppState.addEventListener('change', state => {
if (state !== 'active') {
videoRef.current?.pause?.();
}
});

return () => sub.remove();
}, [videoRef]);
}






Cleanup is mandatory for:





  • AppState / Keyboard listeners


  • setInterval / setTimeout

  • WebSocket / EventEmitter subscriptions

  • Navigation listeners




Prefer useWindowDimensions over manual Dimensions.addEventListener for size changes.









import React, {useEffect, useState} from 'react';
import {FlatList, Text, TextInput} from 'react-native';

function useDebouncedValue(value, delay = 300) {
const [debounced, setDebounced] = useState(value);

useEffect(() => {
const id = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(id);
}, [value, delay]);

return debounced;
}

function SearchScreen() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const debounced = useDebouncedValue(query);

useEffect(() => {
if (!debounced.trim()) {
setResults([]);
return;
}

let alive = true;
api.search(debounced).then(data => {
if (alive) setResults(data);
});

return () => {
alive = false;
};
}, [debounced]);

return (
<>
<TextInput value={query} onChangeText={setQuery} placeholder="Search" />
<FlatList
data={results}
keyExtractor={item => String(item.id)}
renderItem={({item}) => <Text>{item.title}</Text>}
/>
</>
);
}









useEffect vs data libraries
























Approach Best for

useEffect + useState
Simple one-off fetch, learning, tiny screens
React Query / TanStack Query / SWR Cache, retry, focus refetch, deduping, pagination
Custom useFetch
Shared loading/error shape across a few screens


Rule of thumb: If you are inventing cache keys, stale-while-revalidate, or focus refetch by hand — use a query library.









6. useLayoutEffect — before paint (use carefully)



Runs synchronously after DOM/native updates, before paint. In RN, use it when you must measure/layout before the user sees a flash.






Scenario: Scroll to a message without flicker






import {useLayoutEffect, useRef} from 'react';
import {FlatList} from 'react-native';

function Chat({messages}) {
const listRef = useRef(null);

useLayoutEffect(() => {
if (messages.length === 0) return;
listRef.current?.scrollToEnd({animated: false});
}, [messages.length]);

return <FlatList ref={listRef} data={messages} /* ... */ />;
}






Prefer useEffect for network/AppState. Prefer useLayoutEffect for measurement / scroll position that would otherwise flash.









7. useRef — mutable box & native handles






What it is



useRef holds a mutable .current that does not trigger re-render when changed.






Scenario A: Focus the next input






import React, {useRef} from 'react';
import {TextInput} from 'react-native';

function LoginForm() {
const passwordRef = useRef(null);

return (
<>
<TextInput
returnKeyType="next"
blurOnSubmit={false}
onSubmitEditing={() => passwordRef.current?.focus()}
/>
<TextInput ref={passwordRef} secureTextEntry />
</>
);
}









Scenario B: Store latest callback without re-subscribing






import {useCallback, useLayoutEffect, useRef} from 'react';

function useEvent(handler) {
const ref = useRef(handler);
useLayoutEffect(() => {
ref.current = handler;
});
return useCallback((...args) => ref.current(...args), []);
}






Useful when an effect should always see the latest props/state but you don’t want to re-bind listeners every render. (Same idea as React’s experimental useEffectEvent.)






Scenario C: Ignore first render (skip initial effect)






import {useEffect, useRef} from 'react';

function useDidUpdate(effect, deps) {
const mounted = useRef(false);

useEffect(() => {
if (!mounted.current) {
mounted.current = true;
return;
}
return effect();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
}






Use sparingly — usually you want the first run too, or you can gate with if (!ready) return inside a normal effect.









8. useMemo & useCallback — performance tools (not defaults)






What they are





  • useMemo(() => value, deps) — cache an expensive computed value


  • useCallback(fn, deps) — cache a function identity



Do not wrap everything. Use when:




  • Computation is heavy, or

  • A child / FlatList depends on referential equality






Scenario: Filter a large list






function FavoritesScreen({products, query}) {
const filtered = useMemo(() => {
const q = query.trim().toLowerCase();
return products.filter(p => p.title.toLowerCase().includes(q));
}, [products, query]);

return <FlatList data={filtered} /* ... */ />;
}









Scenario: Stable renderItem / handlers for list rows



useCallback alone is not enough if the row still receives a new inline function every time. Pair with React.memo and pass a stable handler + id.




import React, {memo, useCallback} from 'react';
import {FlatList, Pressable, Text} from 'react-native';

const ProductRow = memo(function ProductRow({item, onOpen}) {
return (
<Pressable onPress={() => onOpen(item.id)}>
<Text>{item.title}</Text>
</Pressable>
);
});

function ProductList({products, onOpen}) {
const renderItem = useCallback(
({item}) => <ProductRow item={item} onOpen={onOpen} />,
[onOpen],
);

const keyExtractor = useCallback(item => String(item.id), []);

return (
<FlatList
data={products}
renderItem={renderItem}
keyExtractor={keyExtractor}
initialNumToRender={10}
windowSize={7}
/>
);
}






Interview line




useCallback does not make the function faster — it keeps the same function reference so React.memo children / list cells can skip re-renders. Without memo, useCallback often changes nothing.










9. useReducer — when state has multiple transitions






Scenario: Checkout / form wizard






const initial = {step: 0, address: null, payment: null, submitting: false};

function reducer(state, action) {
switch (action.type) {
case 'NEXT':
return {...state, step: state.step + 1};
case 'SET_ADDRESS':
return {...state, address: action.payload};
case 'SET_PAYMENT':
return {...state, payment: action.payload};
case 'SUBMIT_START':
return {...state, submitting: true};
case 'SUBMIT_DONE':
return {...state, submitting: false};
default:
return state;
}
}

function CheckoutScreen() {
const [state, dispatch] = useReducer(reducer, initial);

const submit = async () => {
dispatch({type: 'SUBMIT_START'});
try {
await api.checkout(state);
dispatch({type: 'SUBMIT_DONE'});
} catch {
dispatch({type: 'SUBMIT_DONE'});
}
};

// render steps from state.step
}






When to prefer useReducer over many useStates




  • Next state depends on previous in complex ways

  • Multiple fields update together

  • You want named actions (easier to test/log)









10. useContext — share without prop drilling






Scenario: Auth across the app



Stabilize actions with useCallback, then memoize the context value so consumers don’t re-render when nothing meaningful changed.




import React, {
createContext,
useCallback,
useContext,
useMemo,
useState,
} from 'react';
import {Pressable, Text} from 'react-native';

const AuthContext = createContext(null);

export function AuthProvider({children}) {
const [user, setUser] = useState(null);

const login = useCallback(async creds => {
setUser(await api.login(creds));
}, []);

const logout = useCallback(() => setUser(null), []);

const value = useMemo(
() => ({user, login, logout}),
[user, login, logout],
);

return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error('useAuth must be used within AuthProvider');
return ctx;
}

function ProfileScreen() {
const {user, logout} = useAuth();
return (
<Pressable onPress={logout}>
<Text>Log out {user?.name}</Text>
</Pressable>
);
}






RN tips




  • Put providers above NavigationContainer so every screen can read them

  • Split contexts (Auth vs Theme) — a theme toggle shouldn’t re-render auth-heavy trees

  • Don’t put high-frequency values (scroll offset) in context — use refs / Reanimated









11. React Native platform hooks



These are the hooks you won’t see in plain React DOM apps as often.






useWindowDimensions — responsive layout






import {useWindowDimensions, View} from 'react-native';

function Hero() {
const {width, height} = useWindowDimensions();
const isTablet = width >= 768;

return (
<View style={{height: height * 0.4, padding: isTablet ? 32 : 16}}>
{/* ... */}
</View>
);
}






Scenario: Phone vs tablet padding / columns without listening to Dimensions manually.






useColorScheme — light / dark






import {useColorScheme} from 'react-native';

function ThemedScreen() {
const scheme = useColorScheme(); // 'light' | 'dark' | null
const bg = scheme === 'dark' ? '#111' : '#fff';
return <View style={{flex: 1, backgroundColor: bg}} />;
}









useSafeAreaInsets — notches & home indicator






import {useSafeAreaInsets} from 'react-native-safe-area-context';

function AppHeader() {
const insets = useSafeAreaInsets();
return (
<View style={{paddingTop: insets.top, height: 56 + insets.top}}>
<Text>Home</Text>
</View>
);
}






Scenario: Custom header under a notch — padding with insets.top instead of hardcoding 44.






Keyboard-aware pattern (custom hook)






import {useEffect, useState} from 'react';
import {Keyboard, Platform} from 'react-native';

function useKeyboardHeight() {
const [height, setHeight] = useState(0);

useEffect(() => {
const showEvt =
Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
const hideEvt =
Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';

const showSub = Keyboard.addListener(showEvt, e => {
setHeight(e.endCoordinates.height);
});
const hideSub = Keyboard.addListener(hideEvt, () => setHeight(0));

return () => {
showSub.remove();
hideSub.remove();
};
}, []);

return height;
}












12. Navigation hooks (React Navigation)






Scenario: Open details + read params






import {useNavigation, useRoute, useFocusEffect} from '@react-navigation/native';
import {useCallback, useState} from 'react';

function HomeScreen() {
const navigation = useNavigation();

return (
<Pressable
onPress={() =>
navigation.navigate('Details', {id: 42, title: 'React Native'})
}>
<Text>Open details</Text>
</Pressable>
);
}

function DetailsScreen() {
const route = useRoute();
const {id, title} = route.params;

return <Text>{title} (#{id})</Text>;
}









Scenario: Refetch every time screen gains focus



useEffect([]) runs on mount only. In a tab navigator, the screen may stay mounted — use focus.




function FavoritesScreen() {
const [items, setItems] = useState([]);

useFocusEffect(
useCallback(() => {
let alive = true;
api.getFavorites().then(data => {
if (alive) setItems(data);
});
return () => {
alive = false;
};
}, []),
);

return <FlatList data={items} /* ... */ />;
}









Scenario: Prevent leaving with unsaved changes






import React, {useEffect, useState} from 'react';
import {Alert, TextInput} from 'react-native';
import {useNavigation} from '@react-navigation/native';

function EditProfileScreen() {
const navigation = useNavigation();
const [name, setName] = useState('');
const [dirty, setDirty] = useState(false);

useEffect(() => {
const unsub = navigation.addListener('beforeRemove', e => {
if (!dirty) return;
e.preventDefault();
Alert.alert('Discard changes?', '', [
{text: 'Stay', style: 'cancel'},
{
text: 'Discard',
style: 'destructive',
onPress: () => navigation.dispatch(e.data.action),
},
]);
});
return unsub;
}, [navigation, dirty]);

return (
<TextInput
value={name}
onChangeText={text => {
setName(text);
setDirty(true);
}}
/>
);
}












13. Custom hooks — the real power move



A custom hook = reusable stateful logic with a use name.






useFetch — loading / error / data



Teaching version with AbortController. For production apps with cache/retry, prefer TanStack Query.




import {useEffect, useState} from 'react';

function useFetch(url) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
if (!url) return;

const controller = new AbortController();
setLoading(true);
setError(null);

fetch(url, {signal: controller.signal})
.then(r => {
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json();
})
.then(json => setData(json))
.catch(e => {
if (e.name === 'AbortError') return;
setError(e);
})
.finally(() => {
if (!controller.signal.aborted) setLoading(false);
});

return () => controller.abort();
}, [url]);

return {data, error, loading};
}









useToggle






import {useCallback, useState} from 'react';

function useToggle(initial = false) {
const [on, setOn] = useState(initial);
const toggle = useCallback(() => setOn(v => !v), []);
const setTrue = useCallback(() => setOn(true), []);
const setFalse = useCallback(() => setOn(false), []);
return {on, toggle, setTrue, setFalse};
}

// Modal open/close
const modal = useToggle();
// modal.on, modal.toggle(), modal.setFalse()









useAppState






import {useEffect, useState} from 'react';
import {AppState} from 'react-native';

function useAppState() {
const [state, setState] = useState(AppState.currentState);

useEffect(() => {
const sub = AppState.addEventListener('change', setState);
return () => sub.remove();
}, []);

return state; // 'active' | 'background' | 'inactive'
}









Scenario: Refresh token only while app is active






function useSessionRefresh() {
const appState = useAppState();

useEffect(() => {
if (appState !== 'active') return;
const id = setInterval(() => {
api.refreshToken();
}, 60_000);
return () => clearInterval(id);
}, [appState]);
}









Prefer abort/alive over useIsMounted



A global isMounted flag is an older pattern. Prefer:





  • AbortController for fetch

  • Local let alive = true inside the effect that owns the async work



That keeps cancellation next to the subscription — easier to reason about under Strict Mode.









14. Putting it together — screen scenario



Feature: Explore screen with search, pull-to-refresh, responsive grid, and safe area.




import React, {useCallback, useState} from 'react';
import {
ActivityIndicator,
FlatList,
Text,
TextInput,
useWindowDimensions,
View,
} from 'react-native';
import {useFocusEffect, useNavigation} from '@react-navigation/native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';

function ExploreScreen() {
const navigation = useNavigation();
const insets = useSafeAreaInsets();
const {width} = useWindowDimensions();
const numColumns = width >= 768 ? 3 : 2;

const [query, setQuery] = useState('');
const debounced = useDebouncedValue(query);
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [error, setError] = useState(null);

const load = useCallback(async (q, {signal} = {}) => {
const data = await api.getExplore(q, {signal});
setItems(data);
}, []);

useFocusEffect(
useCallback(() => {
const controller = new AbortController();
setLoading(true);
setError(null);

load(debounced, {signal: controller.signal})
.catch(e => {
if (e.name !== 'AbortError') setError(e.message);
})
.finally(() => {
if (!controller.signal.aborted) setLoading(false);
});

return () => controller.abort();
}, [load, debounced]),
);

const onRefresh = useCallback(async () => {
setRefreshing(true);
try {
await load(debounced);
} catch (e) {
setError(e.message);
} finally {
setRefreshing(false);
}
}, [load, debounced]);

const onOpen = useCallback(
id => navigation.navigate('ExploreDetails', {id}),
[navigation],
);

const renderItem = useCallback(
({item}) => <ProductCard item={item} onOpen={onOpen} />,
[onOpen],
);

return (
<View style={{flex: 1, paddingTop: insets.top}}>
<TextInput value={query} onChangeText={setQuery} placeholder="Search" />
{loading && !refreshing ? <ActivityIndicator /> : null}
{error ? <Text>{error}</Text> : null}
<FlatList
data={items}
numColumns={numColumns}
key={numColumns}
refreshing={refreshing}
onRefresh={onRefresh}
renderItem={renderItem}
keyExtractor={item => String(item.id)}
/>
</View>
);
}






Hooks used and why








































Hook Role
useNavigation Open details
useSafeAreaInsets Notch padding
useWindowDimensions Responsive columns
useDebouncedValue Don’t hit API every keystroke
useFocusEffect Reload + abort when leaving / blurring
useCallback Stable load / onOpen / renderItem
useState Query, items, loading, error, refreshing








15. Common pitfalls (RN edition)





  1. Missing cleanup → duplicate listeners / double intervals after navigate


  2. Fetch without abort/alive → state updates after unmount; messy Strict Mode


  3. useEffect instead of useFocusEffect → stale data on tabs


  4. useCallback without React.memo on rows → often zero benefit


  5. Unstable dependency objects → effect loops (const options = {page: 1} each render)


  6. useMemo / useCallback everywhere → noise, harder reviews, little gain


  7. Derived data in state → sync bugs; compute inline or with useMemo


  8. Conditional hooks → React crash / mismatched hook order


  9. Ignoring Debug vs Release → perf conclusions from Debug are often false


  10. Putting scroll position in Context → re-renders the tree every frame




// BAD — derived state duplication
const [items, setItems] = useState([]);
const [count, setCount] = useState(0);
useEffect(() => setCount(items.length), [items]);

// GOOD
const count = items.length;

// BAD — new object every render retriggers effect
useEffect(() => {
load(options);
}, [{page: 1}]);

// GOOD
const page = 1;
useEffect(() => {
load({page});
}, [page]);












16. Quick revision checklist



Before merging a screen




  • [ ] All hooks at top level

  • [ ] Picked the right hook for the job (§3)

  • [ ] Effects that subscribe also unsubscribe

  • [ ] Async work uses AbortController or an alive flag

  • [ ] Tab screens that need fresh data use useFocusEffect

  • [ ] Lists: stable keys; useCallback only with memo rows if needed

  • [ ] No derived data mirrored into extra useState

  • [ ] Safe areas for custom headers

  • [ ] Smoke-test rotate + background/foreground once



Hook cheat card (full tables in §3)














































































Need Hook Where
UI value useState Screen / component
Complex transitions useReducer Wizards / multi-field flows
Sync external system useEffect Screen / custom hook
Measure / no flash useLayoutEffect Lists / chat scroll
Native node or mutable box useRef Inputs / lists / timers
Shared app data useContext Root provider → screens
Expensive derive useMemo Filters / heavy calc
Stable function useCallback
FlatList / memo children
Screen focus refetch useFocusEffect Tab / stack screens
Notch / home bar useSafeAreaInsets Headers / full-screen UI
Orientation size useWindowDimensions Responsive layout
Light / dark useColorScheme Theme colors
Reused logic Custom useX
src/hooks/








Conclusion



Hooks are how React Native screens think: state for UI, effects for the outside world, refs for imperative islands, context for shared app data, and custom hooks for reuse.



Takeaways




  1. Start from what you need, then pick the hook (§3) — don’t default everything to useState + useEffect

  2. Learn the Rules of Hooks once — they never change

  3. Cleanup is part of the feature: listeners, timers, and abort in-flight fetch

  4. Prefer useFocusEffect for navigation-aware data loading


  5. useCallback helps only when paired with React.memo / list identity needs

  6. Extract custom hooks when logic repeats; use a query library when you need cache/retry



Next steps




  1. Pick one screen and map each state/effect to the §3 decision table

  2. Add abort/alive + focus-aware fetching where missing

  3. Extract one custom hook this week (useDebouncedValue or useAppState)

  4. Audit one FlatList: stable keys, memoized rows, measured useCallback









Call to action



Which hook still feels confusing in React Native — useFocusEffect, dependency arrays, or knowing when not to use useMemo?



Comment below with a scenario from your app (chat, feed, auth, video). If this guide helped, share it with a teammate who still treats every bug as “wipe node_modules” — or still fetches only with a lonely useEffect([]) on tab screens.



Want a follow-up? Tell me: Reanimated hooks, TanStack Query + RN, or testing hooks with RNTL.

2. Cyber Threat Intelligence & Forensik

IoC Intelligence (1 Indikatoren)
insets[.]top
CTI Threat Relationship Graph2 Knoten / 1 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten React Native Hooks in Depth — Examples, Scenarios & Production Patterns

Thematisch verwandte Begriffe: React, Native, Hooks, Depth · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag