Claude-skill-registry data-and-state-management

Core patterns for data fetching, state management, and user preferences. Use when implementing new features that require getting data from Our APIs, Morpho API, on-chain states or managing shared state.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/data-and-state-management" ~/.claude/skills/majiayu000-claude-skill-registry-data-and-state-management && rm -rf "$T"
manifest: skills/data/data-and-state-management/SKILL.md
source content

Quick Reference

State Management Decision Tree

External Data (API, blockchain) → React Query
User Preferences (persist across refresh) → Zustand + persist
Shared UI State (modals, selections, operations) → Zustand
Computed/Derived → useMemo Hook
Local UI State (single component) → useState

Detailed Patterns

Data Flow

1. Try Morpho API (if network supported)
   ↓ fails
2. Fallback to The Graph Subgraph
   ↓ optional
3. Enhance with on-chain RPC data

React Query (External Data)

Location:

src/hooks/queries/use{Entity}Query.ts

export const useMarketsQuery = () => {
  return useQuery({
    queryKey: ['markets'],
    queryFn: fetchMarkets,
    staleTime: 5 * 60 * 1000,
  });
};

// Usage
const { data, isLoading } = useMarketsQuery();

Zustand + Persist (User Preferences)

Location:

src/stores/use{Feature}{State}.ts

export const useMarketsFilters = create(
  persist(
    (set) => ({
      selectedNetwork: null,
      setSelectedNetwork: (network) => set({ selectedNetwork: network }),
    }),
    { name: 'monarch_store_marketsFilters' }
  )
);

// Usage - separate selectors for primitives
const network = useMarketsFilters((s) => s.selectedNetwork);
const setNetwork = useMarketsFilters((s) => s.setSelectedNetwork);

Zustand (Shared UI State)

Location:

src/stores/use{Feature}Store.ts

// Modal state
export const useVaultModalStore = create((set) => ({
  isOpen: false,
  open: () => set({ isOpen: true }),
  close: () => set({ isOpen: false }),
}));

// Selection state
export const useTableSelectionStore = create((set) => ({
  selectedIds: [],
  toggleSelection: (id) => set((state) => ({
    selectedIds: state.selectedIds.includes(id)
      ? state.selectedIds.filter((i) => i !== id)
      : [...state.selectedIds, id]
  })),
  clearSelection: () => set({ selectedIds: [] }),
}));

Derived Data Hooks

Location:

src/hooks/use{Processed|Filtered}{Entity}.ts

export const useFilteredMarkets = () => {
  const { data } = useMarketsQuery();
  const searchQuery = useMarketsFilters((s) => s.searchQuery);

  return useMemo(() => {
    return data
      .filter((m) => m.symbol.includes(searchQuery))
      .sort((a, b) => b.tvl - a.tvl);
  }, [data, searchQuery]);
};

Anti-Patterns

// ❌ Don't fetch in Context
const Provider = () => {
  useEffect(() => { fetch().then(setData); }, []);
  return <Context.Provider value={data}>{children}</Context.Provider>;
};
// ✅ Use React Query
const useDataQuery = () => useQuery({ queryKey: ['data'], queryFn: fetch });
// ❌ Don't create objects in selectors (infinite loop)
const filters = useStore((s) => s.filters ?? { min: '0' });
// ✅ Return primitives
const min = useStore((s) => s.filters?.min ?? '0');
// ❌ Don't use useState for shared state
const [modalOpen, setModalOpen] = useState(false);
// Then pass through 3 levels of props...
// ✅ Use Zustand for shared UI state
const useModalStore = create((set) => ({
  isOpen: false,
  open: () => set({ isOpen: true }),
}));
// ❌ Don't chain useEffect
useEffect(() => { setFiltered(data.filter(...)); }, [data]);
useEffect(() => { setSorted(filtered.sort(...)); }, [filtered]);
// ✅ Use useMemo
const processed = useMemo(() => data.filter(...).sort(...), [data]);