Skip to content

2025

Understanding the Lifecycle of useRef in React and Avoiding Stale Reference Bugs

React's useRef hook is often used to persist values across renders without triggering re-renders. However, it's important to understand that useRef only survives re-renders, not re-mounts. This distinction can lead to subtle and hard-to-diagnose bugs, especially when working with closures or asynchronous logic.

Re-renders vs Remounts: Understanding the Difference

Before diving into the useRef lifecycle, it's crucial to understand the difference between re-renders and remounts in React:

Re-renders occur when React updates an existing component instance in response to state or props changes. During a re-render:

  • The component function runs again
  • New JSX is generated and compared to the previous render
  • The DOM is updated only where necessary (reconciliation)
  • Component instance and all hooks (including useRef) maintain their identity
  • Triggered by: setState, props changes from parent, context changes, or forceUpdate

Remounts occur when React completely destroys a component instance and creates a new one. During a remount:

  • The old component instance is fully discarded
  • All hooks are re-initialized from scratch
  • The entire component lifecycle starts over (mount → render → effect)
  • Any cleanup functions from the old instance are executed
  • Triggered by: key prop changes, conditional rendering (condition && <Component />), route navigation, or parent component structure changes

The key insight is that useRef survives re-renders but gets reset during remounts, which can lead to stale reference bugs when closures outlive the component instance.

useRef is tied to the component instance

When a component re-renders due to state or props changes, useRef maintains its identity. However, when the component is re-mounted - for example, due to a change in the key prop, conditional rendering, or route transitions - a new component instance is created, and the old one is discarded. The useRef is then re-initialized along with the rest of the component.

This becomes problematic when an earlier closure outlives the component itself and references the old ref object. Since that ref will never be updated again, the closure holds a stale reference. These are a few example cases where a closure might outlive the component: 1. Uncancelled timers: setTimeout or setInterval callbacks that don't have their timers cleared in cleanup functions 2. Promises: Asynchronous operations that resolve after the component unmounts 3. Recursive calls: Functions that call themselves with timeouts or in response to events or processing results

Example: Logging a stale ref due to a remount

import { useEffect, useRef } from 'react';

function MyComponent({ id }) {
  const latestIdRef = useRef(id);

  useEffect(() => {
    latestIdRef.current = id;
  }, [id]);

  useEffect(() => {
    const interval = setInterval(() => {
      console.log('Latest ID from ref:', latestIdRef.current);
    }, 60000); // 1 minute

    // Without cleanup: interval continues after unmount with stale ref
    // return () => clearInterval(interval);
  }, []);

  return <div>Component with ID: {id}</div>;
}

And rendered like this:

<MyComponent key={userId} id={userId} />

In this setup, whenever userId changes, the key change forces a full unmount and remount. The new component gets a new useRef object, but the old setInterval continues to run and holds a reference to the stale ref from the destroyed component instance. When the interval eventually fires (after 1 minute in this example), it logs the outdated value because the old ref is never updated again. As a result, the logged value is stale.

Timeline example: 1. Component mounts with userId: 1, interval starts with 1-minute delay 2. After 30 seconds, userId changes to 2, triggering unmount/remount, but the interval is not cancelled upon unmount 3. New component instance created with userId: 2 and a new latestIdRef 4. After the full minute, the old interval fires, logging the stale value 1 instead of the current 2

This problem becomes more pronounced with longer timeouts or when remounts happen frequently relative to the interval duration.

Recommendations

  • Avoid relying on useRef for values that need to persist across component unmounts and remounts.
  • Be cautious when using closures that capture useRef values, particularly in asynchronous or interval-based logic.
  • If persistence across remounts is required, consider lifting state to a shared parent component, using context, or using external state management.
  • Prefer lifecycle-safe patterns such as useEffect cleanups or observable subscriptions that are scoped to the component's active instance.

Understanding how useRef behaves with respect to the component lifecycle can help prevent subtle bugs and ensure your application logic remains predictable.

Further readings

Writing Root Cause Analysis with Confluence and Data Visualization

When conducting a post-incident Root Cause Analysis (RCA), the ability to tell a clear, data-driven story is crucial. This post explores how to leverage modern observability tools and Confluence to create comprehensive RCA reports that drive meaningful discussions and prevent future incidents.

The Power of Visual Data in RCA

During incident investigation, raw logs and metrics can be overwhelming. Tools like Datadog and Kibana transform this data into actionable insights through visualization. Here's how to effectively use them:

Datadog Integration

The Datadog Connector for Confluence app allows you to:

  • Embed real-time metrics dashboards
  • Share performance graphs directly in your RCA report on Confluence

Kibana Visualization

With Kibana Cloud Connector for Confluence, you can:

  • Embed log analysis visualizations
  • Share error rate graphs
  • Display traffic patterns during the incident

Real-World Example: Database Scaling Incident

At Wavether, we recently worked with a client and used these tools during our analysis of a database scaling incident for the client:

Incident: The client's product database experienced significant performance degradation during peak traffic.

Visualization approach:

  • Used Datadog to correlate user traffic spikes with database connection saturation
  • Embedded Kibana log analysis showing specific query patterns that triggered the issue
  • Created a timeline visualization mapping customer reports against backend metrics

Outcome: The visualizations clearly showed that the connection pooling settings were inadequate for the new traffic patterns, leading to a configuration update that prevented future incidents.

Best Practices for RCA Documentation

  1. Timeline Visualization

    • Create a timeline using Datadog's Events feature
    • Mark key events and correlate with metrics
  2. System Impact Analysis

    • Embed Datadog dashboards showing:
      • Error rates
      • Latency spikes
      • Resource utilization
  3. Log Analysis

    • Use Kibana visualizations to show:
      • Error patterns
      • Affected services
      • User impact
  4. Root Cause Confirmation

    • Correlate multiple data sources
    • Support findings with embedded graphs
    • Link to relevant monitoring dashboards

Adapting RCAs for Different Audiences

When creating data visualizations for RCAs, consider tailoring them based on the audience:

  • For Technical Teams: Include detailed metrics, code snippets, and specific technical indicators
  • For Product Management: Focus on user impact metrics and feature correlation
  • For Executive Stakeholders: Emphasize business impact, recovery time, and prevention strategies

Common Pitfalls to Avoid

  • Visualization overload: Too many graphs can obscure the story
  • Inconsistent time ranges: Ensure all visualizations use the same time boundaries
  • Missing context: Always annotate unusual patterns or key events
  • Correlation confusion: Remember that correlation doesn't imply causation

Tips for Maximum Impact

  • Keep dashboards focused and relevant
  • Annotate graphs to highlight key points
  • Use consistent time ranges across visualizations
  • Include links to live dashboards for further investigation

Getting Started

Try these tools today for free:

  1. Datadog Connector for Confluence
  2. Kibana Cloud Connector for Confluence

By combining these powerful visualization tools with Confluence's collaborative features, you can create clear, data-driven RCA documents that help prevent future incidents and improve system reliability.

Conclusion

Modern observability tools have transformed how we approach RCA. By leveraging Datadog and Kibana's visualization capabilities within Confluence, teams can create more effective, data-driven reports that lead to actionable insights and improved system reliability.