<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gourav Bhardwaj</title>
    <description>The latest articles on DEV Community by Gourav Bhardwaj (@iaamgourav).</description>
    <link>https://dev.to/iaamgourav</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1194555%2F94441528-1a18-4610-9278-f4766bbe94b9.png</url>
      <title>DEV Community: Gourav Bhardwaj</title>
      <link>https://dev.to/iaamgourav</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/iaamgourav"/>
    <language>en</language>
    <item>
      <title>LWC Kept Showing Old Record Data Until I Stopped Using connectedCallback</title>
      <dc:creator>Gourav Bhardwaj</dc:creator>
      <pubDate>Wed, 25 Feb 2026 07:20:18 +0000</pubDate>
      <link>https://dev.to/iaamgourav/lwc-kept-showing-old-record-data-until-i-stopped-using-connectedcallback-30kc</link>
      <guid>https://dev.to/iaamgourav/lwc-kept-showing-old-record-data-until-i-stopped-using-connectedcallback-30kc</guid>
      <description>&lt;p&gt;You've built a dynamic data table in LWC. It pulls records via Apex, responds to a lookup field, and works perfectly on first load. Then a user navigates to a different record, the component re-renders — and the old data is still sitting there. You refresh. It updates. The bug is real but intermittent, and it makes no sense because you're not even using &lt;code&gt;@AuraEnabled(cacheable=true)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The culprit, more often than not, is &lt;code&gt;connectedCallback&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This article walks through a reusable data table architecture I built recently, the cache issue I ran into, and why switching to &lt;code&gt;@api&lt;/code&gt; setter methods fixed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Component Architecture
&lt;/h2&gt;

&lt;p&gt;The setup is two components: a parent that holds filter controls (a lookup field, search input, etc.) and a child that renders the table. The child is intentionally generic — swap the lookup reference and it returns different data. You can drop it anywhere.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Parent: c-record-filter-form --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;c-lookup-field&lt;/span&gt; &lt;span class="na"&gt;onselect=&lt;/span&gt;&lt;span class="s"&gt;{handleLookupSelect}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/c-lookup-field&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;c-dynamic-data-table&lt;/span&gt;
    &lt;span class="na"&gt;record-id=&lt;/span&gt;&lt;span class="s"&gt;{recordId}&lt;/span&gt;
    &lt;span class="na"&gt;lookup-id=&lt;/span&gt;&lt;span class="s"&gt;{selectedLookupId}&lt;/span&gt;
    &lt;span class="na"&gt;object-api-name=&lt;/span&gt;&lt;span class="s"&gt;"Account"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/c-dynamic-data-table&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Child: c-dynamic-data-table --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;if:true=&lt;/span&gt;&lt;span class="s"&gt;{tableData}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;lightning-datatable&lt;/span&gt;
      &lt;span class="na"&gt;key-field=&lt;/span&gt;&lt;span class="s"&gt;"Id"&lt;/span&gt;
      &lt;span class="na"&gt;data=&lt;/span&gt;&lt;span class="s"&gt;{tableData}&lt;/span&gt;
      &lt;span class="na"&gt;columns=&lt;/span&gt;&lt;span class="s"&gt;{columns}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/lightning-datatable&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The child receives &lt;code&gt;recordId&lt;/code&gt; and &lt;code&gt;lookupId&lt;/code&gt; as public properties and is responsible for fetching its own data. Clean separation, reusable anywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why connectedCallback Seems Like the Right Place to Call Apex
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;connectedCallback&lt;/code&gt; fires when the component is inserted into the DOM. If you need data on load, it feels natural to put your Apex call there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// c-dynamic-data-table.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LightningElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lwc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;getTableData&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@salesforce/apex/DataTableController.getTableData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DynamicDataTable&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LightningElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;api&lt;/span&gt; &lt;span class="nx"&gt;recordId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;api&lt;/span&gt; &lt;span class="nx"&gt;lookupId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;tableData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;getTableData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;recordId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recordId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lookupId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookupId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works on the first load. The problem surfaces during navigation.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Stale Data Problem
&lt;/h2&gt;

&lt;p&gt;In Salesforce, when you navigate between records — say from Account A to Account B — LWC components in the record page don't always get destroyed and recreated from scratch. The framework reuses the existing component instance and updates its &lt;code&gt;@api&lt;/code&gt; properties.&lt;/p&gt;

&lt;p&gt;When that happens, &lt;code&gt;connectedCallback&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; fire again. It already ran when the component was first connected to the DOM. So even though &lt;code&gt;recordId&lt;/code&gt; is now pointing to Account B, your Apex call never re-runs. The table still shows Account A's data.&lt;/p&gt;

&lt;p&gt;This is not a caching issue with &lt;code&gt;@AuraEnabled(cacheable=true)&lt;/code&gt;. The Apex method isn't even being called. The component is simply reusing its previous state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User navigates: Account A → Account B
↓
@api recordId updates to Account B's Id
↓
connectedCallback does NOT re-fire
↓
fetchData() never called
↓
Table still shows Account A data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Fix: @api Setter Methods
&lt;/h2&gt;

&lt;p&gt;The right approach is to trigger &lt;code&gt;fetchData()&lt;/code&gt; whenever a property changes, not just when the component first mounts. &lt;code&gt;@api&lt;/code&gt; setters handle exactly this.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;api&lt;/span&gt; &lt;span class="nx"&gt;recordId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;api&lt;/span&gt; &lt;span class="nx"&gt;lookupId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;_recordId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;_lookupId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;api&lt;/span&gt;
&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;recordId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_recordId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;recordId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_recordId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchDataIfReady&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;api&lt;/span&gt;
&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;lookupId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_lookupId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;lookupId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_lookupId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchDataIfReady&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;fetchDataIfReady&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_recordId&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_lookupId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;getTableData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;recordId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_recordId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lookupId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_lookupId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every time the parent passes a new &lt;code&gt;recordId&lt;/code&gt; or &lt;code&gt;lookupId&lt;/code&gt; — whether on first load or after navigation — the setter fires, checks that both values are present, and calls Apex. No stale data, no missed refreshes.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fetchDataIfReady&lt;/code&gt; guard is important. Both setters can fire independently, and you don't want to call Apex with a &lt;code&gt;null&lt;/code&gt; lookupId because &lt;code&gt;recordId&lt;/code&gt; updated first.&lt;/p&gt;




&lt;h2&gt;
  
  
  connectedCallback vs @api Setter: When to Use Which
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;connectedCallback&lt;/code&gt; when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your component doesn't depend on &lt;code&gt;@api&lt;/code&gt; properties to fetch data&lt;/li&gt;
&lt;li&gt;You're setting up event listeners, timers, or subscriptions&lt;/li&gt;
&lt;li&gt;Data only needs to load once and won't change based on parent property updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;@api&lt;/code&gt; setter when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The component receives data identifiers as &lt;code&gt;@api&lt;/code&gt; properties&lt;/li&gt;
&lt;li&gt;The component lives on a record page and navigates between records&lt;/li&gt;
&lt;li&gt;You need the component to react every time a property value changes, not just on mount&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  One More Thing: Property Initialization Order
&lt;/h2&gt;

&lt;p&gt;When a component first renders, LWC sets &lt;code&gt;@api&lt;/code&gt; properties before calling &lt;code&gt;connectedCallback&lt;/code&gt;. So if you were relying on &lt;code&gt;connectedCallback&lt;/code&gt; to read &lt;code&gt;this.recordId&lt;/code&gt;, it would actually be set by that point — which is part of why the bug is subtle. It works on first load, breaks on navigation. That inconsistency is what makes it hard to catch during development.&lt;/p&gt;

&lt;p&gt;Setters eliminate this ambiguity entirely. The logic lives where the property lives, and it runs every time — first load or not.&lt;/p&gt;




&lt;p&gt;The reusable table pattern itself is solid. The mistake was assuming the component lifecycle on navigation mirrors a fresh page load. Once you account for how LWC handles property updates during navigation, setters are the cleaner and more reliable choice for any component that fetches data based on incoming properties.&lt;/p&gt;

</description>
      <category>salesforce</category>
      <category>lwc</category>
      <category>lightningwebcomponent</category>
      <category>sfdc</category>
    </item>
  </channel>
</rss>
