DEV Community

Cathy Lai
Cathy Lai

Posted on

useRef() and how it prevents flicker in a Map component

Understanding useRef and When to Ditch useState

The useRef hook is a pillar of modern React development, especially when working with mobile interfaces in React Native. It's the essential tool for escaping the declarative world of React and dipping your toes into low-level component control.


1. What is useRef? (The Elevator Pitch)

useRef is a built-in React Hook that provides an object whose value persists across component re-renders. It serves two main purposes:

  1. Component Reference (The Analogy): To get a direct handle on a component instance (like a TextInput or ScrollView) so you can manually control action-oriented functions.
  2. Component-Specific Memory: To hold any value (a counter, a timer ID, etc.) that needs to change without forcing the component to re-render. This memory is persistent between renders.

It returns a simple object: $\{ \text{current}: \text{value} \}$.


2. 🆚 The Web Analogy: useRef vs. document.getElementById

For web developers transitioning to React Native, the most helpful way to understand useRef is to think of it as your replacement for reaching into the Document Object Model (DOM).

Feature React Native with useRef Web JavaScript with ID
Action Programmatically Focus a TextInput. Programmatically Focus an <input>.
Mechanism You assign the ref object to the ref prop: <TextInput **ref={inputRef}** /> You assign an ID to the element: <input **id="target"** />
Access inputRef.current.focus() document.getElementById("target").focus()

The core similarity is that both methods allow you to manually control the underlying UI element, bypassing the standard declarative flow of data via props and state.


3. 🗺️ Real-World Example: Controlling a Map Component

This is where useRef truly shines. When working with complex third-party components like MapView (from react-native-maps), you often need to trigger animations or change the view without causing a costly re-render.

The Problem with Using State

If you tried to manage the map's location using state, it would look like this:

// DON'T DO THIS for animation/control!
const [region, setRegion] = useState({ /* ...coords... */ });

// To move the map:
setRegion({ new_coords }); // This causes a re-render!
Enter fullscreen mode Exit fullscreen mode

The issue: Changing the map region via state often causes the entire MapView component to re-render from scratch. This is slow, can be visually jarring, and typically prevents the smooth, native-level camera animations that the map component is capable of.

The Solution with useRef

By using useRef, we gain access to the map's internal action-oriented functions, such as animateToRegion(), which uses the native platform's efficient animation logic.

import React, { useRef } from 'react';
import MapView from 'react-native-maps'; // Hypothetical import

const MapControlDemo = () => {
  // 1. Declare the ref
  const mapRef = useRef(null); 

  const flyToAuckland = () => {
    const aucklandCoords = { latitude: -36.84, longitude: 174.76, latitudeDelta: 0.1, longitudeDelta: 0.1 };

    if (mapRef.current) {
      // Manually control the map instance and call its action-oriented function
      mapRef.current.animateToRegion(aucklandCoords, 1000); // Smooth 1000ms animation
    }
  };

  return (
    <View style={styles.container}>
      {/* 4. Attach the ref to the MapView component */}
      <MapView ref={mapRef} style={styles.map} initialRegion={{ /* ... */ }} />
      <Button title="Fly to Auckland" onPress={flyToAuckland} />
    </View>
  );
};
Enter fullscreen mode Exit fullscreen mode

Using this approach:

  • We don't call setState, so the component doesn't re-render.
  • We manually control the native map instance directly: "Animate to this new location."

4. 💡 Other Common Uses of useRef

Beyond accessing component instances, useRef is your go-to for managing any data that needs to persist across renders without triggering a new render cycle.

Use Case Why useRef is Used
Timers & Intervals To store the ID returned by setInterval or setTimeout so you can clear it later (in a useEffect cleanup function).
Previous Values To store the component's previous prop or state value, allowing you to compare current and former values in a useEffect hook.
Component Counters To count how many times a component has rendered. Using state would cause an infinite render loop; useRef does not.
Event Handlers To access the latest version of a state variable inside an event handler without re-creating the function (advanced performance optimization).

In short, if you need a static piece of Component-Specific Memory that doesn't interact with the visual rendering, useRef is your answer.

Top comments (0)