All skills
asyrafhussin avatar

/state-management

@10bb16e

React Query and Zustand patterns for state management. Use when implementing data fetching, caching, mutations, or client-side state. Triggers on tasks involving useQuery, useMutation, Zustand stores, caching, or state management.

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/state-management

This session only. Nothing lands on disk.

rulesrq-initial-data.md

≈1.4k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Initial Data for Cache Seeding

Impact: MEDIUM

Initial data seeds the query cache with known data before or instead of fetching. It's cached immediately and affects query staleness, unlike placeholderData.

Bad Example

// Anti-pattern: Using initialData for loading placeholders
const { data } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
  initialData: { name: 'Loading...' }, // This is cached as real data!
  // If staleTime > 0, this fake data will be served
});

// Anti-pattern: Initial data without staleTime consideration
const { data } = useQuery({
  queryKey: ['product', productId],
  queryFn: () => fetchProduct(productId),
  initialData: partialProduct, // From list view
  // Without initialDataUpdatedAt, React Query doesn't know how old this is
});

// Anti-pattern: Initial data that might be stale
function ProductDetail({ product }: { product: Product }) {
  const { data } = useQuery({
    queryKey: ['product', product.id],
    queryFn: () => fetchProduct(product.id),
    initialData: product,
    staleTime: 5 * 60 * 1000, // 5 minutes
    // Product from props might be hours old, but won't refetch for 5 minutes
  });

  return <ProductView product={data} />;
}

Good Example

// SSR/SSG: Seed cache with server-fetched data
// In Next.js pages
export async function getServerSideProps() {
  const queryClient = new QueryClient();

  await queryClient.prefetchQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
  });

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  };
}

// Client component uses the pre-seeded cache
function UserProfile({ userId }: { userId: string }) {
  const { data } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
    // Data is already in cache from SSR
  });

  return <Profile user={data} />;
}

// Use initialData with proper staleness tracking
function ProductDetail({ listProduct }: { listProduct: ListProduct }) {
  const { data: product } = useQuery({
    queryKey: ['product', listProduct.id],
    queryFn: () => fetchProduct(listProduct.id),
    initialData: listProduct,
    initialDataUpdatedAt: listProduct.fetchedAt, // Track when list was fetched
    staleTime: 60 * 1000, // 1 minute
    // Will refetch if listProduct.fetchedAt is older than staleTime
  });

  return <ProductView product={product} />;
}

// Seeding from another query's cache
function UserDetail({ userId }: { userId: string }) {
  const queryClient = useQueryClient();

  const { data: user } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
    initialData: () => {
      const usersCache = queryClient.getQueryData<User[]>(['users']);
      return usersCache?.find(u => u.id === userId);
    },
    initialDataUpdatedAt: () => {
      // Get the timestamp of the users list query
      return queryClient.getQueryState(['users'])?.dataUpdatedAt;
    },
  });

  return <UserProfile user={user} />;
}

// Conditional initial data based on source
interface ProductDetailProps {
  productId: string;
  initialProduct?: Product;
  productTimestamp?: number;
}

function ProductDetail({
  productId,
  initialProduct,
  productTimestamp,
}: ProductDetailProps) {
  const { data } = useQuery({
    queryKey: ['product', productId],
    queryFn: () => fetchProduct(productId),
    ...(initialProduct && {
      initialData: initialProduct,
      initialDataUpdatedAt: productTimestamp,
    }),
    staleTime: 30 * 1000,
  });

  return <Product data={data} />;
}

// Pre-seeding cache on application load
async function initializeApp() {
  const queryClient = new QueryClient();

  // Pre-fetch critical data
  await Promise.all([
    queryClient.prefetchQuery({
      queryKey: ['currentUser'],
      queryFn: fetchCurrentUser,
    }),
    queryClient.prefetchQuery({
      queryKey: ['config'],
      queryFn: fetchAppConfig,
    }),
  ]);

  return queryClient;
}

// Direct cache manipulation for known data
function handleRouteData(data: RouteData) {
  // Seed cache with data from route/navigation state
  queryClient.setQueryData(['resource', data.id], data.resource, {
    updatedAt: data.timestamp,
  });
}

Why

  1. SSR Hydration: Initial data enables seamless hydration of server-rendered content into the React Query cache.

  2. Navigation Optimization: Data from list views can seed detail view caches, eliminating loading states.

  3. Offline Support: Known data can be pre-seeded to support offline-first experiences.

  4. Perceived Performance: Instant data display from cache feels faster than waiting for network requests.

  5. Staleness Tracking: initialDataUpdatedAt ensures proper refetching when seeded data is old.

  6. Type Safety: Initial data must match the query return type, catching errors at compile time.

Initial Data vs Placeholder Data:

Feature initialData placeholderData
Cached Yes No
Affects staleTime Yes No
Triggers background refetch If stale Always
Use case Known valid data Temporary UI
With updatedAt Track staleness N/A

Best practices:

  • Always use initialDataUpdatedAt when initial data might be stale
  • Use placeholderData for temporary display data
  • Use initialData for valid data from another source (SSR, cache, route state)
  • Consider data completeness (list items may have fewer fields than detail views)

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill provides secure and comprehensive documentation for managing application state using React Query and Zustand. It includes critical security warnings against storing sensitive data in local storage and promotes best practices for data fetching, caching, and error handling. No malicious patterns or security vulnerabilities were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    32 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 10bb16e. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 7 months ago
metadata
{
  "author": "agent-skills",
  "version": "1.1.0"
}

README badge

README badge for asyrafhussin/agent-skills/state-management