How to Retain Previous Values During Angular Resource Refetch

3 min read

Have you ever noticed a brief flicker or a flash of empty data on your Angular UI when a resource is refetching? It's a common scenario: your application initiates a data fetch, and for a moment, the old data disappears before the new data arrives, leaving a less-than-ideal user experience.

While it makes sense for a resource to report undefined or empty during a refetch—after all, it genuinely doesn't have the new value yet—many applications benefit from simply continuing to display the last known good value until the fresh data is ready. This prevents that jarring visual discontinuity.

The Problem with UI Flickers

Imagine you're viewing a list of products. You apply a filter, triggering a new fetch. Without intervention, your product list might momentarily vanish, only to reappear with the filtered results. This "data disappearing act" can be disruptive and make your application feel less polished.

The Elegant Solution: linkedSignal

Fortunately, there's a neat workaround using a utility often found in modern Angular state patterns, like linkedSignal (credits to a helpful discussion on GitHub). This pattern allows you to create a derived signal that intelligently prioritizes new data while gracefully falling back to previous data during a refetch.

What it Does

This approach essentially creates a "view" signal (productsForView in this example) that monitors your actual resource signal (productsValue). When the resource is actively refetching and its value might be undefined or null, productsForView will continue to emit the last successfully loaded value until the new data is available.

Why it's Useful

  • Improved User Experience: Eliminates flickering or flashing of empty states during data refetches.
  • Smoother Transitions: Your UI remains stable and populated, providing a seamless experience.
  • Simple Implementation: Integrates cleanly into your existing state management.

How to Use It

Here’s how you can integrate withLinkedState and linkedSignal into your Angular store or component's state definition:

// Assume you're fetching products reactively using a resource
withResource((store) => ({
    products: store.productsApi.productsResource(store.filters), // products.value will be store.productsValue
    // ... other resources
})),

// Now, make sure the previous value is retained till the new one comes in
withLinkedState((store) => ({
    productsForView: linkedSignal<Product[]>({ // Specify the type of your data
      source: store.productsValue, // The actual signal holding your fetched product array
      computation: (source, previous) => {
        // If 'source' (new data) has a value, use it.
        // Otherwise, if 'previous' (old data) has a value, use its .value.
        // Otherwise, default to an empty array.
        return source ?? previous?.value ?? [];
      },
  }),
})),

Let's break down the linkedSignal part:

  • source: store.productsValue: This is the actual signal from your resource (store.products.value) that holds the data currently being fetched. When productsResource is refetching, store.productsValue might temporarily be undefined or null.
  • computation: (source, previous) => { ... }: This is the core logic.
    • source ?? ...: If the source signal currently has a value (meaning the new data has arrived), that value is returned immediately.
    • previous?.value ?? ...: If source is null or undefined (indicating an ongoing refetch or initial empty state), the computation falls back to previous. previous here refers to the last successfully emitted value of the productsForView signal itself. We access its .value property.
    • []: As a final fallback, if neither source nor previous?.value has a value (e.g., on initial load when no data has ever been fetched), it defaults to an empty array.

By employing linkedSignal with this simple computation, you ensure that your UI always has something to show, gracefully transitioning from old data to new data without any awkward blanks. It's a small change that makes a big difference in application polish.


Full code (with firebase, stripe backend) on my shop: https://zoaibkhan.com/shop/angular-ecommerce?utm_source=linked-state-snippet

You may also like...