Sync/Async
Global State Manager
Inside/Outside React

Yes, another state management library alternative 👀

Inspiration

Inspired by Zustand, TanStack Query, and SWR

This library was highly-inspired by Zustand (opens in a new tab), TanStack Query (opens in a new tab), and SWR (opens in a new tab).
Its DX will be very similar to Zustand's. [See comparison]

Read Full Article →

What Will You Get

  • A global state manager just like Zustand.
  • A data fetching state manager just like TanStack Query & SWR, but tastes like Zustand.
  • A smaller bundle size package.
import { create } from 'zustand'; // 3.3 kB (gzipped: 1.5 kB)
import { createStore } from 'floppy-disk'; // 1.4 kB (gzipped: 750 B) 🎉
 
import { QueryClient, QueryClientProvider, useQuery, useInfiniteQuery, useMutation } from '@tanstack/react-query'; // 31.7 kB kB (gzipped: 9.2 kB)
import { createQuery, createMutation } from 'floppy-disk'; // 9.7 kB (gzipped: 3.3 kB) 🎉
🍽
Have a taste?
› Zustand + ReactQuery: demo-zustand-react-query.vercel.app (opens in a new tab) (Total: 309.21 kB)
› Floppy Disk: demo-floppy-disk.vercel.app (opens in a new tab) (Total: 272.63 kB 🎉)

Store

Render: 0
Count: 3
Shape: square
Render: 0
Count: 3
Render: 0
Shape: square
Store
Render: 0
Only re-render when"count value is a multiple of 5" is changed
Render: 0
🔕
Count: 3
Shape: square
Render: 0
import { createStore } from 'floppy-disk';
 
type MyStore = {
  count: number;
  shape: string;
  increment: () => void;
  changeShape: () => void;
};
export const useMyStore = createStore<MyStore>(({ set, get }) => ({
  count: 3,
  shape: 'square',
  increment: () => set((prev) => ({ count: prev.count + 1 })),
  changeShape: () => {
    const { shape } = get();
    let nextShape;
    if (shape === 'square') nextShape = 'circle';
    else if (shape === 'circle') nextShape = 'triangle';
    else nextShape = 'square';
    set({ shape: nextShape });
  },
}));

Query & Mutation

import { createQuery } from 'floppy-disk';
 
type Pokemon = { id: string; name: string };
 
// Without param
const useLatestPokemonQuery = createQuery<undefined, Pokemon>(
  async () => {
    const res = await fetch('/api/pokemons/latest');
    const resJson = await res.json();
    if (res.ok) return resJson;
    throw resJson;
  }
);
 
// With param
const usePokemonDetailQuery = createQuery<{ id: number }, Pokemon>(
  async ({ id }) => {
    const res = await fetch(`/api/pokemons/${id}`);
    const resJson = await res.json();
    if (res.ok) return resJson;
    throw resJson;
  }
);
 
function LatestPokemonPage() {
  const { isLoading, data, error } = useLatestPokemonQuery();
  if (error) return <div>Something went wrong</div>;
  if (isLoading) return <div>Loading...</div>;
  return <div>Latest pokemon is {data.name}</div>;
}
 
function PokemonDetailPage({ id }: { id: number }) {
  const { isLoading, data, error } = usePokemonDetailQuery({ id });
  if (error) return <div>Something went wrong</div>;
  if (isLoading) return <div>Loading...</div>;
  return <div>Pokemon: {data.name}</div>;
}

Features

  • Create Store
    • Get/set store inside/outside component
    • Very simple way to customize the reactivity (state update subscription)
    • Support middleware
    • Set state interception
    • Store event (onSubscribe, onUnsubscribe, etc.)
    • Use store as local state manager
  • Create Stores
    • Same as store, but controlled with a store key
  • Create Query & Mutation
    • Backend agnostic (support GraphQL & any async function)
    • TypeScript ready
    • SSR/SSG support
    • Custom reactivity (we choose when to re-render)
    • Create query
      • Dedupe multiple request
      • Auto-fetch on mount or manual (lazy query)
      • Enable/disable query
      • Serve stale data while revalidating
      • Retry on error (customizable)
      • Optimistic update
      • Invalidate query
      • Reset query
      • Query with param (query key)
      • Paginated/infinite query
      • Prefetch query
      • Suspense mode
      • Fetch from inside/outside component
      • Get query state inside/outside component
    • Create mutation
      • Mutate from inside/outside component
      • Get mutation state inside/outside component