nanostores/query

How to use a `FetcherStore` with a `computed()` atom

Bwvolleyball opened this issue · 2 comments

I'm looking for a little advice on the best way to use one or more fetcher stores from a computed() atom - I'm struggling with what to use to actually subscribe to the fetcher.

Example:

const ids = atom(/* some dymanic thing that returns an array of ids */);

const requestFactory = (id: string | undefined) => {
  const store = createFetcherStore<Response>(['/api/v1/thing/', atom(id)]);
  return store;
}

computed(ids, (ids) =>
    ids.map((id) => {
        const singleStore = requestFactory(id);
        /* How to use `singleStore` here though? */
    }).reduce(/* Something with all these ids against the API call... */);
);

You can try to avoid the dynamic creation of query atoms and process all the logic within the fetcher function:

const $ids = atom([1,2,3]);

// need convert ids into string because only primitive values should be as part of key 
const $idsKey = computed($ids, (ids) => ids.join(','));

const $responses = createFetcherStore<Response[]>(['/api/v1/thing/', $idsKey], {
  fetcher: (url, idsKey) => {
    // convert stringified ids into array
    const ids = idsKey.split(',');
    // call all API requests and return array of responses
    return Promise.all(
      ids.map((id) =>
        fetch(`${url}/${id}`).then((response) => {
          if (response.ok) {
            return response.json();
          }
          throw new Error();
        }),
      ),
    );
  },
});

const $result = computed($responses, (responses) => responses.reduce(/* Do something with all responses of API call */));

@Bwvolleyball Yo!

Fetcher stores are just smarter versions of basic atoms from nanostores. They have all the same methods to work with: .get(), .listen(), .subscribe() and others.


I wouldn't suggest going the route @mrfratello offered, because it kind of makes the cache very inefficient: if you change a single id in the array, you end up wasting the whole cache record and re-requesting too much stuff. Generally, you need one request per one store at the same time. There are exceptions to this rule (e.g., when API is not well-built and you actually need to perform a chain of operations that yield one API record), but they are truly rare.

I'm not entirely sure what you're trying to do though. Typically we use state managers with some frameworks. It's true that sometimes you have an unpredictable/dynamic amount of stores that cannot be encoded within JS module. In such cases I typically go for the hack that's covered in Readme. Essentially, create fetcher stores dynamically in the component using useState's factory signature (in React that is; in other frameworks you can just memo them or create during mount phase) and subscribe to them right there. Cache is singleton, and fetcher stores are just an interface to it, so even if you create 10 stores with the same cache keys, requests will be deduplicated, data will be in sync, etc.

Lmk if you have any questions!