fix(phase2): resolve code quality issues in ItemPhotoUpload (act warnings, toast cleanup, dual error handling)

Fixed 3 critical code quality issues:

1. Act() warnings in tests (9 tests):
   - Wrapped all async state updates in act() blocks in usePhotoUpload.test.ts
   - Tests using waitFor() now properly await state updates within act()
   - All 21 tests pass with zero act() warnings

2. Missing toast cleanup on unmount:
   - Added toastIdRef to track pending toast IDs
   - Added cleanup useEffect that dismisses toasts on component unmount
   - Prevents memory leaks and orphaned toast notifications

3. Dual error reporting channels (lines 23-28):
   - Removed useEffect that synced hook error to local state AND called onError callback
   - Now syncs hook error to local state only (for display)
   - Parent components rely on hook error state, reducing dual-path confusion
   - Toast error calls are explicit in catch block

Test Results:
- Frontend: 312/312 tests passing (includes 21 photo upload tests)
- Act() warnings: Eliminated
- No regressions introduced
This commit is contained in:
2026-04-21 12:50:44 +03:00
parent db9aafd47f
commit 5a64dadc1e
7 changed files with 69 additions and 42 deletions

View File

@@ -18,25 +18,35 @@ export default function ItemPhotoUpload({
const fileInputRef = useRef<HTMLInputElement>(null);
const cameraInputRef = useRef<HTMLInputElement>(null);
const [localError, setLocalError] = useState<string | null>(null);
const toastIdRef = useRef<string | null>(null);
// Update local error when hook error changes
// Sync hook error to local state for display
React.useEffect(() => {
if (error) {
setLocalError(error);
onError(error);
}
}, [error, onError]);
}, [error]);
// Cleanup: dismiss pending toasts on unmount
React.useEffect(() => {
return () => {
if (toastIdRef.current) {
toast.dismiss(toastIdRef.current);
}
};
}, []);
const handleFileSelect = async (file: File) => {
setLocalError(null);
if (!file) return;
const toastId = toast.loading('Uploading...');
toastIdRef.current = toast.loading('Uploading...');
try {
const photo = await upload(file, itemId);
toast.success('Photo uploaded successfully', { id: toastId });
toast.success('Photo uploaded successfully', { id: toastIdRef.current });
toastIdRef.current = null;
onUploadSuccess(photo);
// Reset file inputs
@@ -49,7 +59,8 @@ export default function ItemPhotoUpload({
} catch (err: any) {
const errorMsg = err.message || 'Upload failed';
setLocalError(errorMsg);
toast.error(errorMsg, { id: toastId });
toast.error(errorMsg, { id: toastIdRef.current || undefined });
toastIdRef.current = null;
onError(errorMsg);
}
};