A social knowledge tool for researchers built on ATProto

refactor: optimize edit page with useMemo and useCallback to prevent re-renders

Co-authored-by: aider (anthropic/claude-sonnet-4-20250514) <aider@aider.chat>

+33 -27
+33 -27
src/webapp/app/(authenticated)/collections/[collectionId]/edit/page.tsx
··· 1 1 'use client'; 2 2 3 - import { useState, useEffect } from 'react'; 3 + import { useState, useEffect, useMemo, useCallback } from 'react'; 4 4 import { useRouter, useParams } from 'next/navigation'; 5 5 import { 6 6 Container, ··· 23 23 const params = useParams(); 24 24 const collectionId = params.collectionId as string; 25 25 26 - // Create API client instance 27 - const apiClient = new ApiClient( 28 - process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3000', 29 - () => getAccessToken(), 26 + // Memoize API client to prevent recreation on every render 27 + const apiClient = useMemo( 28 + () => 29 + new ApiClient( 30 + process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3000', 31 + () => getAccessToken(), 32 + ), 33 + [], 30 34 ); 31 35 32 36 const [collection, setCollection] = ··· 38 42 const [error, setError] = useState<string | null>(null); 39 43 const [success, setSuccess] = useState(false); 40 44 41 - // Load collection data 42 - useEffect(() => { 43 - const loadCollection = async () => { 44 - try { 45 - setIsLoading(true); 46 - const response = await apiClient.getCollectionPage(collectionId); 47 - setCollection(response); 48 - setName(response.name); 49 - setDescription(response.description || ''); 50 - } catch (err) { 51 - setError('Failed to load collection'); 52 - console.error('Error loading collection:', err); 53 - } finally { 54 - setIsLoading(false); 55 - } 56 - }; 45 + // Memoize the load collection function to prevent recreation 46 + const loadCollection = useCallback(async () => { 47 + if (!collectionId) return; 57 48 58 - if (collectionId) { 59 - loadCollection(); 49 + try { 50 + setIsLoading(true); 51 + setError(null); 52 + const response = await apiClient.getCollectionPage(collectionId); 53 + setCollection(response); 54 + setName(response.name); 55 + setDescription(response.description || ''); 56 + } catch (err) { 57 + setError('Failed to load collection'); 58 + console.error('Error loading collection:', err); 59 + } finally { 60 + setIsLoading(false); 60 61 } 61 62 }, [collectionId, apiClient]); 62 63 63 - const handleSave = async () => { 64 + // Load collection data 65 + useEffect(() => { 66 + loadCollection(); 67 + }, [loadCollection]); 68 + 69 + const handleSave = useCallback(async () => { 64 70 if (!name.trim()) { 65 71 setError('Collection name is required'); 66 72 return; ··· 88 94 } finally { 89 95 setIsSaving(false); 90 96 } 91 - }; 97 + }, [name, description, collectionId, apiClient, router]); 92 98 93 - const handleCancel = () => { 99 + const handleCancel = useCallback(() => { 94 100 router.push(`/collections/${collectionId}`); 95 - }; 101 + }, [router, collectionId]); 96 102 97 103 if (isLoading) { 98 104 return (