Adventures in Refreshing LWC

Often you may want to refresh from the server to present updates back to your users and this post shows how this can work and some of the pitfalls along the way.

Lets start with an easy example that we can use as a demo. We will set up an apex class that returns a string of the server time and we will present this to the user in an lwc component. Then we will add a button to refresh the Apex and see if we can get the serverTime to update. Let’s start with the apex class which simply returns a string of the current DateTime :

public with sharing class refreshController {
  @AuraEnabled(cacheable=true)
  public static string serverDateTime() {
    return String.valueOf(DateTime.now());
  }
}

Now lets look at the code for the lwc component.

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>55.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

The javascript file is simple. We are wiring to the apex class, checking if we have data or an error and if all ok setting the serverTime to be the string returned from the Apex call :

import { LightningElement, wire } from "lwc";
import serverDateTime from "@salesforce/apex/refreshController.serverDateTime";

export default class RefreshDemo extends LightningElement {
  serverTime;

  @wire(serverDateTime)
  serverDateTime({ error, data }) {
    if (data) {
      console.log("--serverDateTime --");
      console.log(data);
      this.serverTime = data;
    } else if (error) {
      console.log("--error serverDateTime --");
      console.log(error);
    }
  }
}

Our html is very small:

<template>
  <lightning-card title="Refresh Demo">
    <div class="slds-m-around_small">
      <div>Server Time: {serverTime}</div>
    </div>
  </lightning-card>
</template>

When the component is added to the screen we should get back the server time and show this to the user:

Let’s build this out and add a refresh button and then when this is called run the RefreshApex command on our serverTime. In our html file we add in a simple button for the user to click – so our html file becomes:

<template>
  <lightning-card title="Refresh Demo">
    <div class="slds-m-around_small">
      <div>Server Time: {serverTime}</div>
      <div>
        <lightning-button
          variant="brand"
          label="Refresh"
          title="Refresh"
          onclick={handleRefresh}
        ></lightning-button>
      </div>
    </div>
  </lightning-card>
</template>

On the javascript file we need two additions, the first is to import the refence to the RefreshApex provided by Salesforce and then react to the button click and ask the RefreshApex to update our serverTime.

import { LightningElement, wire } from "lwc";
import { refreshApex } from "@salesforce/apex";
import serverDateTime from "@salesforce/apex/refreshController.serverDateTime";

export default class RefreshDemo extends LightningElement {
  serverTime;

  @wire(serverDateTime)
  serverDateTime({ error, data }) {
    if (data) {
      console.log("--serverDateTime --");
      console.log(data);
      this.serverTime = data;
    } else if (error) {
      console.log("--error serverDateTime --");
      console.log(error);
    }
  }

  handleRefresh() {
    console.log("Refresh Button Clicked");
    refreshApex(this.serverTime);
  }
}

This looks ok on the screen but nothing happens when we click the button. In Dev tools in the console window you will see the code being called.

Our problem is that the lwc is not associating the serverTime attribute with the wired method that is doing the updating. The wire structure is fine but instead of breaking the results in the data and error components instead we need to return one result (that has the data and error properties). This will allow us to refresh that.

It is easier to see the difference in code. So lets changed the wire function:

  @wire(serverDateTime)
  serverDateTime(result) {
    if (result.data) {
      console.log("--serverDateTime --");
      console.log(result.data);
      this.serverTime = result.data;
    } else if (result.error) {
      console.log("--error serverDateTime --");
      console.log(result.error);
    }
  }

This code is now returning a complex object result with properties data and error and it another way to call an Apex class. If you try this it still will not work, because our serverTime is set to the result.data and not the main result. Indeed we will need another variable, that we will hook the refreshApex to.

Below is our final code and we are using the refreshServerTime only to help call the wired function as we are running the refreshApex against the refreshServerTime and not the serverTime. But the serverTime is set when the call the wired function. So now when the user clicks the button then the time is updated because we have asked for the refreshServerTime to be updated and the lwc knows that this is set by the serverDateTime wired function and this in turn will update the serverTime that is actually displayed to the user.

import { LightningElement, wire } from "lwc";
import { refreshApex } from "@salesforce/apex";
import serverDateTime from "@salesforce/apex/refreshController.serverDateTime";

export default class RefreshDemo extends LightningElement {
  serverTime;
  refreshServerTime;

  @wire(serverDateTime)
  serverDateTime(result) {
    this.refreshServerTime = result;
    if (result.data) {
      console.log("--serverDateTime --");
      console.log(result.data);
      this.serverTime = result.data;
    } else if (result.error) {
      console.log("--error serverDateTime --");
      console.log(result.error);
    }
  }

  handleRefresh() {
    console.log("Refresh Button Clicked");
    refreshApex(this.refreshServerTime);
  }
}

As a bit of a bonus to this post what happens if we wanted to automatically update the apex say every second (in this case every second makes sense as it is a clock). We can set up an interval timer to call the server. We can use the connectedCallback to set up the time that will call the refreshApex. We will need a new attribute in the class. The 1000 is every second. There are major problems if your call to server takes longer than the timer, it would be better to say have this every minute in a production environment:


  autoRefreshTimer;

  connectedCallback() {
    this.autoRefreshTimer = setInterval(() => {
      refreshApex(this.refreshServerTime);
    }, 1000);
  }

  disconnectedCallback() {
    clearInterval(this.autoRefreshTimer);
  }

Leave a Reply

Your email address will not be published. Required fields are marked *