fix: Move useEffect before early returns (Rules of Hooks)

Critical bug: Hook called after conditional return

Problem:
- useEffect at line 107 was AFTER early returns (lines 83-102)
- When loading/error states triggered early return
- Hook order changed between renders
- React detected hook order violation
- Component broke with blank screen

Rules of Hooks violation:
 Before:
1. All hooks (useState, useQuery, etc.)
2. Early return if loading
3. Early return if error
4. useEffect (line 107) ← WRONG! After conditional returns

 After:
1. All hooks including ALL useEffects
2. Early return if loading
3. Early return if error
4. Render

Fix:
- Moved useEffect from line 107 to line 62
- Now before any early returns
- Changed product?.meta to productQ.data?.meta
- Hooks always called in same order
- No conditional hook calls

Result:
 Product edit form loads correctly
 No React warnings
 Follows Rules of Hooks
 Consistent hook order every render
This commit is contained in:
dwindown
2025-11-20 22:22:40 +07:00
parent 27d12f47a1
commit fe545a480d
2 changed files with 11 additions and 9 deletions

View File

@@ -1,6 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { __ } from '@/lib/i18n'; import { __ } from '@/lib/i18n';
import { CouponsApi, type Coupon } from '@/lib/api/coupons'; import { CouponsApi, type Coupon } from '@/lib/api/coupons';
import { showErrorToast, showSuccessToast, getPageLoadErrorMessage } from '@/lib/errorHandling'; import { showErrorToast, showSuccessToast, getPageLoadErrorMessage } from '@/lib/errorHandling';
@@ -253,7 +253,9 @@ export default function CouponsIndex() {
/> />
</td> </td>
<td className="p-3"> <td className="p-3">
<div className="font-medium">{coupon.code}</div> <Link to={`/coupons/${coupon.id}`} className="font-medium hover:underline">
{coupon.code}
</Link>
{coupon.description && ( {coupon.description && (
<div className="text-sm text-muted-foreground line-clamp-1"> <div className="text-sm text-muted-foreground line-clamp-1">
{coupon.description} {coupon.description}

View File

@@ -59,6 +59,13 @@ export default function ProductEdit() {
await updateMutation.mutateAsync(payload); await updateMutation.mutateAsync(payload);
}; };
// Sync meta data from product
useEffect(() => {
if (productQ.data?.meta) {
setMetaData(productQ.data.meta);
}
}, [productQ.data?.meta]);
// Set page header with back button and save button // Set page header with back button and save button
useEffect(() => { useEffect(() => {
const actions = ( const actions = (
@@ -103,13 +110,6 @@ export default function ProductEdit() {
const product = productQ.data; const product = productQ.data;
// Sync meta data from product
useEffect(() => {
if (product?.meta) {
setMetaData(product.meta);
}
}, [product?.meta]);
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<ProductForm <ProductForm