Command Palette

Search for a command to run...

React Hooks Review: How They Work in a Mobile Environment

React Hooks: How they work in a mobile environment

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 write likes = 1. You are required to call setLikes(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:

  1. You can freely change the data inside the box.
  2. This change never causes the screen to be re-drawn.
  3. 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.

Related Posts

What is React Native? Why use it for Mobile programming?

Discover the power of React Native and the Expo platform that helps you build mobile apps quickly without the hassle of complex setups.

Guide to Setting Up React Native & Expo Environment in 15 Minutes

Afraid of complex mobile programming environment setups? A detailed guide on using Expo Go and Node.js to run React Native apps instantly.

Detailed Analysis of Expo App Flow from A-Z

Learn how an application starts, master the app/ directory, basic file structures, and parse JSX syntax in an incredibly easy-to-understand way.

Understanding the 5 Most Important Core Components in React Native

A guide on how to use Core Components in React Native. Master View, Text, Image, TextInput, and ScrollView to build any mobile interface.