Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@nanostores/query

Package Overview
Dependencies
Maintainers
4
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@nanostores/query

Tiny remote data fetching library for Nano Stores

  • 0.0.3
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
4.6K
decreased by-3.69%
Maintainers
4
Weekly downloads
 
Created
Source

Nano Stores Query

A tiny data fetcher for Nano Stores.

Sponsored by Evil Martians

Install

npm install nanostores @nanostores/query

Usage

See Nano Stores docs about using the store and subscribing to store’s changes in UI frameworks.

Query

First, we define the context. It allows us to share the default fetcher implementation between all fetcher stores, refetching settings, and allows for simple mocking in tests and stories.

// store/fetcher.ts
import { nanoquery } from '@nanostores/query';

export const [createFetcherStore, createMutatorStore] = nanoquery({
  fetcher: (...keys: string[]) => fetch(keys.join('')).then((r) => r.json()),
});

Second, we create the fetcher store. createFetcherStore returns the usual atom() from Nano Stores, that is reactively connected to all stores passed as keys. Whenever the $currentPostId updates, $currentPost will call the fetcher once again.

// store/posts.ts
import { createFetcherStore } from './fetcher';

export const $currentPostId = atom('');
export const $currentPost = createFetcherStore<Post>(['/api/post/', $currentPostId]);

Third, just use it in your components. createFetcherStore returns the usual atom() from Nano Stores.

// components/Post.tsx
const Post = () => {
  const { data, loading } = useStore($currentPost);
  if (loading) return <>Loading...</>;
  if (!data) return <>Error!</>;

  return <div>{data.content}</div>;
};

createFetcherStore

export const $currentPost = createFetcherStore<Post>(['/api/post/', $currentPostId]);

It accepts two arguments: key and fetcher options.

type KeyParts = undefined | Array<ReadableAtom<string | null | undefined> | string>

Under the hood, nanoquery will get the string values and pass them to your fetcher like this: fetcher(...keyPartsAsStrings). If any atom value is either null or undefined, we never call the fetcher—this is the conditional fetching technique we have.

type Options = {
  // The async function that actually returns the data
  fetcher?: (...keyParts: string[]) => Promise<unknown>;
  // How much time should pass between running fetcher for the exact same key parts
  // default = 4s
  dedupeTime?: number;
  // If we should revalidate the data when the window focuses
  // default = false
  refetchOnFocus?: boolean;
  // If we should revalidate the data when network connection restores
  // default = false
  refetchOnReconnect?: boolean;
  // If we should run revalidation on an interval, in ms
  // default = 0, no interval
  refetchInterval?: number;
}

The same options can be set on the context level where you actually get the createFetcherStore.

createMutatorStore

Mutator basically allows for 2 main things: tell nanoquery what data should be revalidated and optimistically change data. From interface point of view it's essentially a wrapper around your async function with some added functions.

It gets an object with 3 arguments:

  • data is the data you pass to the mutate function;
  • invalidate allows you to mark other keys as stale so they are refetched next time;
  • getCacheUpdater allows you to get current cache value by key and update it with a new value. The key is also invalidated by default.
export const $addComment = createMutatorStore<Comment>(
  async ({ data: comment, invalidate, getCacheUpdater }) => {
    // You can either invalidate the author…
    invalidate(`/api/users/${comment.authorId}`);

    // …or you can optimistically update current cache.
    const [updateCache, post] = getCacheUpdater(`/api/post/${comment.postId}`);
    updateCache({ ...post, comments: [...post.comments, comment] });

    // Even though `fetch` is called after calling `invalidate`, we will only
    // invalidate the keys after `fetch` resolves
    return fetch('…')
  }
);

The usage in component is very simple as well:

const AddCommentForm = () => {
  const { mutate, loading, error } = useStore($addComment);

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        mutate({ postId: "…", text: "…" });
      }}
    >
      <button disabled={loading}>Send comment</button>
      {error && <p>Some error happened!</p>}
    </form>
  );
};

Keywords

FAQs

Package last updated on 07 Mar 2023

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc