title: Handling specific tab state via a nested object in storage for a Chrome extension?
body: <p>In my Chrome extension some data that I need to store is global but other data is local to each tab. Ideally there would be a way to store data per tab but sadly this doesn't exist.</p>
  <p>In storage I'm using a <code>tabs</code> object where each key is a tab ID. Here tab ID1 has a status of 'yes' and tab ID2 is 'no'</p>
  <pre><code>  tabs: {
      1: 'yes',
      2: 'no',
    }
  </code></pre>
  <p>However this becomes unwieldy when I want to override one tab's data. To avoid overriding the other tabs data I need to read <code>tabs</code>, override the property, then write it:</p>
  <pre><code>  chrome.storage.local.get(['tabs'], ({ tabs }) =&gt; {
      tabs[1] = 'maybe';
      chrome.storage.local.set({ tabs });
    });
  </code></pre>
  <p>Listening for changes is also a bit verbose:</p>
  <pre><code>chrome.storage.onChanged.addListener(({ tabs: { newValue, oldValue } }) =&gt; {
    if (newValue[1] !== oldValue[1]) {
      console.log(newValue[1]);
    }
  });
  </code></pre>
  <p>Is there a better way to handle updating nested objects in storage or in generally handling tab specific state?</p>
  <p>The other option I thought of was to save each tab ID as a top level key:</p>
  <pre><code>chrome.storage.local.set({
    101: 'yes',
    102: 'no',
  });
  
  setTimeout(() =&gt; {
    chrome.storage.local.set({ [101]: 'maybe' });
  }, 1000);
  
  chrome.storage.onChanged.addListener(changes =&gt; {
    const newValue = changes[101]?.newValue;
    if (newValue) {
      console.log(newValue);
    }
  });
  </code></pre>
  <p>This is easier to work with but feels like the lack of encapsulation could cause problems. Also while you can remove the relevant data when a tab is deleted this isn't 100% reliable eg if you force close Chrome. So stale data could get left in storage and perhaps over time this would become quite sizable.</p>
  <pre><code>chrome.tabs.onRemoved.addListener(tabId =&gt; {
    chrome.storage.local.remove([`${tabId}`]);
  });
  </code></pre>