![]()
In the previous article, we successfully built a static interface with a "Hello World" screen. But a real mobile app cannot just stand still like a picture. It has to "live": Buttons must count the number of taps, lists must automatically load more data when swiped, and the interface must change when a user logs in.
To make the application respond to these interactions, we need a powerful tool. In the world of React Native, that tool is called React Hooks.
This article will help you master the 3 most important "weapons": useState, useEffect, and useRef. No matter how complex your application is, they are all built on these 3 core foundations.
1. useState: The short-term memory of the UI
The most important concept in React Native is State. State is where a screen's data is stored.
The ultimate rule: Whenever the State changes, React Native will automatically re-render the screen to update the latest interface.
To create State, we use the useState Hook.
Syntax and How it works
Imagine we are making a "Like" button for an article.
import React, { useState } from 'react'
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
export default function LikeButton() {
// Declare a 'likes' state with an initial value of 0
const [likes, setLikes] = useState(0)
return (
<View style={styles.container}>
<Text style={styles.text}>Number of likes: {likes}</Text>
<TouchableOpacity
style={styles.button}
onPress={() => setLikes(likes + 1)} // Update state on press
>
<Text style={styles.buttonText}>❤️ Like</Text>
</TouchableOpacity>
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
text: { fontSize: 20, marginBottom: 20 },
button: { backgroundColor: '#ff4757', padding: 15, borderRadius: 10 },
buttonText: { color: 'white', fontWeight: 'bold' },
})
Dissecting the syntax const [likes, setLikes] = useState(0):
0: This is the initial starting value.likes: This is the variable holding the current value (like reading the data).setLikes: This is a "magical" function. Whenever you want to change the number of likes, you MUST NOT writelikes = 1. You are required to callsetLikes(1). When this function is called, React Native immediately knows that the data has changed and will re-update the text on the phone screen.
2. useEffect: Lifecycle Management & Side Effects
If useState is "memory", then useEffect is the "senses" and "actions" of the application.
useEffect is used to handle Side Effects - meaning things that happen outside of rendering the UI, such as: calling an API to fetch a list of articles from a server, setting a timer, or listening to GPS location.
Core phases of useEffect
The syntax of useEffect takes two parameters: An execution function, and a Dependency Array. Depending on this dependency array, useEffect will operate in 3 different ways:
Way 1: Run ONLY ONCE when the screen is first opened (Empty array []). This is the most common use case, usually for calling an API to fetch initial data.
useEffect(() => {
console.log('The screen has just been opened. Start loading data from the Server...')
// Call API here...
}, []) // An empty array signals it to only run once on Mount
Way 2: Run EVERY TIME a specific State changes (Variables in the array). For example, every time the user changes the search keyword, you want to call the API again to find new items.
const [keyword, setKeyword] = useState('')
useEffect(() => {
console.log(`You just typed the keyword: ${keyword}. Searching...`)
}, [keyword]) // Only runs again when the 'keyword' variable changes
Way 3: Cleaning up trash before leaving (Cleanup Function). In Mobile, memory optimization is vital. If you turn on a timer when entering a screen, you must turn it off when the user switches to another screen (Unmount), otherwise the app will suffer from a Memory Leak and Crash.
useEffect(() => {
const timer = setInterval(() => {
console.log('Tick tock...')
}, 1000)
// The return function inside useEffect acts as a cleanup
return () => {
clearInterval(timer)
console.log('Cleaned up the timer when exiting the screen!')
}
}, [])
3. useRef: The "storage box" that doesn't trigger Re-render
There is a limitation to useState: Every time you use setState to update data, the entire screen will be re-drawn. If you have data that changes continuously but DOES NOT need to be displayed on the UI, using useState will cause severe lag in the app.
That's when useRef shines. useRef is like a data storage box:
- You can freely change the data inside the box.
- This change never causes the screen to be re-drawn.
- The data in the box remains intact and is not lost across each render of the screen.
Most common use case in React Native
Besides storing hidden values, useRef is often used to directly intervene with a Component (Native View). For example: Automatically jumping the mouse cursor into an input field (TextInput).
import React, { useRef } from 'react'
import { View, TextInput, TouchableOpacity, Text } from 'react-native'
export default function InputScreen() {
// Create a reference (container box) to point to the TextInput
const inputRef = useRef(null)
const handleFocus = () => {
// Command the TextInput to pop up the keyboard
inputRef.current.focus()
}
return (
<View style={{ flex: 1, padding: 20 }}>
{/* Attach ref to TextInput */}
<TextInput
ref={inputRef}
style={{ borderWidth: 1, padding: 10, marginBottom: 10 }}
placeholder="Enter your name..."
/>
<TouchableOpacity onPress={handleFocus} style={{ backgroundColor: 'blue', padding: 10 }}>
<Text style={{ color: 'white', textAlign: 'center' }}>Tap to type</Text>
</TouchableOpacity>
</View>
)
}
Conclusion: When to use which?
To avoid confusion when getting started, remember this mantra rule:
- Data changes + Needs to show on screen ➡️ Use
useState. - Need to do side jobs (call API, timer) ➡️ Use
useEffect. - Data changes + DOES NOT need to show on screen OR Need to control a component ➡️ Use
useRef.
With these 3 weapons, you hold 80% of React's core power. Your application can now remember, think, and respond.
However, our screens are currently arranged quite haphazardly. In the next article, we will return to the UI section to learn how to use the most standard "building blocks" of a Native App.