Guide feature development for React applications with architecture focus. Covers Zustand/Redux patterns, IndexedDB usage, component systems, lazy loading strategies, and seamless integration. Use when adding new features, refactoring existing code, or planning major changes.
Guide feature development with React architecture patterns.
Extends: Generic Feature Developer - Read base skill for development workflow, scope assessment, and build vs integrate decisions.
src/
├── components/
│ ├── ui/ # Reusable primitives (Button, Input)
│ ├── features/ # Feature-specific components
│ └── layout/ # Layout components (Header, Sidebar)
├── hooks/ # Custom hooks (useAuth, useStore)
├── stores/ # Zustand stores
├── services/ # API clients, IndexedDB wrappers
├── types/ # TypeScript interfaces
└── lib/ # Utilities
// stores/useFeatureStore.ts
interface FeatureState {
items: Item[];
isLoading: boolean;
// Actions
addItem: (item: Item) => void;
removeItem: (id: string) => void;
}
const useFeatureStore = create<FeatureState>()(
persist(
(set) => ({
items: [],
isLoading: false,
addItem: (item) => set((s) => ({ items: [...s.items, item] })),
removeItem: (id) =>
set((s) => ({
items: s.items.filter((i) => i.id !== id),
})),
}),
{
name: "feature-storage",
version: 1,
migrate: (state, version) => {
// Handle migrations between versions
return state as FeatureState;
},
},
),
);
// Avoid re-renders with selectors
const items = useFeatureStore((state) => state.items);
const addItem = useFeatureStore((state) => state.addItem);
// Shallow compare for objects
import { shallow } from "zustand/shallow";
const { items, isLoading } = useFeatureStore(
(state) => ({ items: state.items, isLoading: state.isLoading }),
shallow,
);
| Use Context | Use Zustand | | ------------------------------ | -------------------------- | | Theme, locale (rarely changes) | Frequently updated data | | Authentication state | Complex state with actions | | Provider already exists | Need persistence | | Prop drilling 1-2 levels | Cross-cutting concern |
// Server state - React Query
const { data, isLoading, error } = useQuery({
queryKey: ["items", userId],
queryFn: () => fetchItems(userId),
staleTime: 5 * 60 * 1000, // 5 minutes
});
// Mutations
const mutation = useMutation({
mutationFn: createItem,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["items"] });
},
});
| Scenario | Solution | | --------------------------- | -------------------------------- | | < 5MB total | localStorage via Zustand persist | | > 5MB total | IndexedDB | | Binary data (images, files) | IndexedDB | | Simple key-value | localStorage | | Complex queries | IndexedDB |
// services/indexedDBService.ts
class IndexedDBService {
private db: IDBDatabase | null = null;
async init() {
return new Promise<void>((resolve, reject) => {
const request = indexedDB.open("AppDB", 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve();
};
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
db.createObjectStore("items", { keyPath: "id" });
};
});
}
async setItem<T>(store: string, value: T): Promise<void> {
// Implementation
}
async getItem<T>(store: string, key: string): Promise<T | null> {
// Implementation
}
}
export const indexedDBService = new IndexedDBService();
// Heavy components (>20KB)
const HeavyChart = lazy(() => import('./HeavyChart'));
const RichTextEditor = lazy(() => import('./RichTextEditor'));
// Pages
const SettingsPage = lazy(() => import('./pages/Settings'));
// Usage with Suspense
<Suspense fallback={<Skeleton />}>
<HeavyChart data={data} />
</Suspense>
// React Router example
const routes = [
{
path: '/dashboard',
element: <DashboardLayout />,
children: [
{
path: 'settings',
lazy: () => import('./pages/Settings'),
},
],
},
];
// hooks/useItems.ts
function useItems() {
const items = useFeatureStore((s) => s.items);
const addItem = useFeatureStore((s) => s.addItem);
const sortedItems = useMemo(
() => [...items].sort((a, b) => b.createdAt - a.createdAt),
[items],
);
return { items: sortedItems, addItem };
}
// hooks/useDashboard.ts
function useDashboard() {
// Local state
const [filter, setFilter] = useState("all");
// Server state
const { data: items } = useQuery({ queryKey: ["items"] });
// Client state
const preferences = usePreferencesStore((s) => s.dashboard);
// Derived
const filteredItems = useMemo(
() => items?.filter((i) => filter === "all" || i.status === filter),
[items, filter],
);
return { filter, setFilter, items: filteredItems, preferences };
}
// Usage: <Tabs><Tabs.List /><Tabs.Panel /></Tabs>
const TabsContext = createContext<TabsContextValue | null>(null);
function Tabs({ children, defaultValue }: TabsProps) {
const [active, setActive] = useState(defaultValue);
return (
<TabsContext.Provider value={{ active, setActive }}>
{children}
</TabsContext.Provider>
);
}
Tabs.List = function TabsList({ children }: { children: ReactNode }) {
return <div role="tablist">{children}</div>;
};
Tabs.Panel = function TabsPanel({ value, children }: TabsPanelProps) {
const { active } = useContext(TabsContext)!;
if (value !== active) return null;
return <div role="tabpanel">{children}</div>;
};
Before Starting:
During Development:
Before Completion:
npm run buildnpx skills add travisjneuman/generic-react-feature-developer下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer