jakearchibald / idb-keyval

A super-simple-small promise-based keyval store implemented with IndexedDB

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

feature request: return new data from update

skyqrose opened this issue · comments

I'm using the atomic update function, but after the update I want to keep the update value. Currently, it returns Promise<void>, and I want it to return Promise<T | undefined>.

My use case is persisting state in a React app. When I update the React state, I also update the data in IDB, and I want to keep IDB and React in sync.

There are two workarounds, and I'm using both:

  • Keep the data in javascript state (which I'm doing anyway) and then also call the update function on the JS state. This works, but I'm worried that the state could drift apart.
  • Reload the data from IDB after every update (and overwrite any data in React). This keeps React and IDB in sync, but means I'm making two calls to IDB. It doesn't cause any race conditions for me but it could for other use cases.

My workaround (roughly):

const [state, setState] = useState<T | undefined>(undefined)
// ...
async () => {
  setState((oldData) => update(oldData));
  await IDB.update(key, update);
  const newData = await IDB.get(key);
  setState(newData);
}

I would prefer to do:

const [state, setState] = useState<T | undefined>(undefined)
// ...
async () => {
  const newData = await IDB.update(key, update);
  setState(newData);
}

I think this feature would be especially useful for people who aren't keeping the data around in their app state, and just want to do something with the data after an update.

It would technically be a breaking change. People probably aren't doing anything with the specific void value that update currently returns, but it could cause typescript builds to fail if the signature changes. One way to mitigate that would be to add a new separate updateAndGet function, and leave update unchanged.

I ran into another example:

const data = await IDB.get(key);
if (isOutdated(data)) {
  await IDB.del(key);
  return null;
} else {
  return data;
}

The get and del aren't atomic, so there's a race condition if someone else writes other data to that key inbetween. I'd like to do the get, check, and delete in a single update:

const data = await IDB.update(key, (data) => {
  if (isOutdated(data)) {
    return null;
  else {
    return data;
  }
});
return data;

(setting to null and deleting aren't quite the same thing but that's not important for this example).