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-select-transform.md

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

Select Option for Data Transformation

Impact: HIGH

The select option transforms or filters query data before it reaches the component. It provides derived data without affecting the cached data and optimizes re-renders.

Bad Example

// Anti-pattern: Transforming data in component body
function UserList() {
  const { data: users } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  });

  // Transformation runs on every render
  const activeUsers = users?.filter(user => user.isActive);
  const sortedUsers = activeUsers?.sort((a, b) => a.name.localeCompare(b.name));

  return <List items={sortedUsers} />;
}

// Anti-pattern: Transforming in queryFn (affects cache)
const { data } = useQuery({
  queryKey: ['users'],
  queryFn: async () => {
    const users = await fetchUsers();
    // Cache only contains filtered data - can't access inactive users
    return users.filter(user => user.isActive);
  },
});

// Anti-pattern: Using useMemo for simple transformations
function UserStats() {
  const { data: users } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  });

  // Unnecessary useMemo when select would suffice
  const userCount = useMemo(() => users?.length ?? 0, [users]);
  const adminCount = useMemo(
    () => users?.filter(u => u.role === 'admin').length ?? 0,
    [users]
  );

  return <Stats total={userCount} admins={adminCount} />;
}

Good Example

// Use select for filtering
function ActiveUserList() {
  const { data: activeUsers } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    select: (users) => users.filter(user => user.isActive),
  });

  return <List items={activeUsers} />;
}

// Use select for transformation with memoization
function UserList() {
  const { data: sortedUsers } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    select: useCallback(
      (users: User[]) =>
        [...users]
          .filter(user => user.isActive)
          .sort((a, b) => a.name.localeCompare(b.name)),
      []
    ),
  });

  return <List items={sortedUsers} />;
}

// Use select for extracting specific fields
function UserCount() {
  const { data: count } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    select: (users) => users.length,
  });

  // Component only re-renders when count changes, not when users change
  return <span>Total users: {count}</span>;
}

// Reusable selector functions
const userSelectors = {
  all: (users: User[]) => users,
  active: (users: User[]) => users.filter(u => u.isActive),
  admins: (users: User[]) => users.filter(u => u.role === 'admin'),
  byId: (id: string) => (users: User[]) => users.find(u => u.id === id),
  count: (users: User[]) => users.length,
  names: (users: User[]) => users.map(u => u.name),
};

// Usage with reusable selectors
function AdminList() {
  const { data: admins } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    select: userSelectors.admins,
  });

  return <List items={admins} />;
}

// Complex transformation with parameters
function FilteredUserList({ role, status }: FilterProps) {
  const selector = useCallback(
    (users: User[]) =>
      users.filter(
        user =>
          (role === 'all' || user.role === role) &&
          (status === 'all' || user.status === status)
      ),
    [role, status]
  );

  const { data: filteredUsers } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    select: selector,
  });

  return <List items={filteredUsers} />;
}

// Combining data from multiple sources
function UserWithPosts({ userId }: { userId: string }) {
  const { data: userName } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    select: (users) => users.find(u => u.id === userId)?.name,
  });

  const { data: postCount } = useQuery({
    queryKey: ['posts', userId],
    queryFn: () => fetchUserPosts(userId),
    select: (posts) => posts.length,
  });

  return (
    <div>
      {userName} has {postCount} posts
    </div>
  );
}

Why

  1. Cache Integrity: The full API response stays in cache; select transforms data only for the specific component.

  2. Render Optimization: When select returns the same value (via structural sharing), the component doesn't re-render.

  3. Code Reuse: Selector functions can be shared across components using the same cached data.

  4. Performance: Select runs only when the source data changes, not on every render.

  5. Separation of Concerns: Keep API responses clean and handle view-specific transformations at the component level.

  6. TypeScript Support: Select provides proper type narrowing for transformed data.

Important notes:

  • Use useCallback to memoize select functions that depend on external values
  • Select receives the cached data, so it runs after successful fetches
  • Multiple components with different selects share the same cache entry
  • Structural sharing means primitive returns (numbers, strings) prevent unnecessary re-renders

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