fix: resolve infinite loop in useEffect/useCallback hooks

- useProdutos: replaced useCallback([currentPage]) with useRef + useCallback([])
- useEntregas: same fix for listarEntregas
- produtos/page: removed unstable listarProdutos from useEffect deps
- entregas/page: removed unstable listarEntregas from useEffect deps

Root cause: useCallback depended on currentPage but setCurrentPage inside caused circular dependency.
This commit is contained in:
Tiago Yamamoto 2026-03-04 20:57:00 -06:00
parent 11c93ae584
commit b0ffceefa8
4 changed files with 32 additions and 17 deletions

View file

@ -32,6 +32,7 @@ const statusIcons = {
}; };
function EntregasContent() { function EntregasContent() {
console.log('[ENTREGAS] 🔄 Component render');
const router = useRouter(); const router = useRouter();
const [user, setUser] = useState<any>(null); const [user, setUser] = useState<any>(null);
const [filtroStatus, setFiltroStatus] = useState<string>("todos"); const [filtroStatus, setFiltroStatus] = useState<string>("todos");
@ -55,6 +56,7 @@ function EntregasContent() {
// Verificação de autenticação // Verificação de autenticação
useEffect(() => { useEffect(() => {
console.log('[ENTREGAS] 🔑 Auth useEffect triggered');
const checkAuth = async () => { const checkAuth = async () => {
try { try {
const storedToken = localStorage.getItem('access_token'); const storedToken = localStorage.getItem('access_token');
@ -96,7 +98,9 @@ function EntregasContent() {
// Carregar entregas quando usuário for autenticado // Carregar entregas quando usuário for autenticado
useEffect(() => { useEffect(() => {
console.log('[ENTREGAS] 📦 Data fetch useEffect triggered', { user: !!user, userLoading });
if (user && !userLoading) { if (user && !userLoading) {
console.log('[ENTREGAS] 📦 Calling listarEntregas...');
listarEntregas({ listarEntregas({
page: 1, page: 1,
search: termoBusca.trim() || undefined, search: termoBusca.trim() || undefined,
@ -106,7 +110,8 @@ function EntregasContent() {
console.error("❌ Erro ao carregar entregas:", error); console.error("❌ Erro ao carregar entregas:", error);
}); });
} }
}, [user, userLoading, listarEntregas]); // eslint-disable-next-line react-hooks/exhaustive-deps
}, [user, userLoading]);
const handleSearch = async () => { const handleSearch = async () => {
limparErro(); limparErro();
@ -697,6 +702,9 @@ function EntregasContent() {
); );
} }
export default function EntregasPage() { const EntregasPage = () => {
console.log('[ENTREGAS] 🔄 Component render');
return <EntregasContent />; return <EntregasContent />;
} }
export default EntregasPage;

View file

@ -10,6 +10,7 @@ import ProdutoList from '@/components/ProdutoList';
import { useProdutos, ProdutoFormData } from '@/hooks/useProdutos'; import { useProdutos, ProdutoFormData } from '@/hooks/useProdutos';
const GestaoProdutos = () => { const GestaoProdutos = () => {
console.log('[PRODUTOS] 🔄 Component render');
const router = useRouter(); const router = useRouter();
const [user, setUser] = useState<Models.User<Models.Preferences> | null>(null); const [user, setUser] = useState<Models.User<Models.Preferences> | null>(null);
const [editing, setEditing] = useState<Models.Document | null>(null); const [editing, setEditing] = useState<Models.Document | null>(null);
@ -45,7 +46,8 @@ const GestaoProdutos = () => {
}; };
initializeUser(); initializeUser();
}, [activeTab, router, listarProdutos]); // eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeTab]);
const handleFormSubmit = async (formData: ProdutoFormData): Promise<boolean> => { const handleFormSubmit = async (formData: ProdutoFormData): Promise<boolean> => {
if (editing) { if (editing) {

View file

@ -1,4 +1,4 @@
import { useState, useCallback } from 'react'; import { useState, useCallback, useRef } from 'react';
import { entregasApiService, EntregaBff, EntregasBffParams } from '@/services/entregasApiService'; import { entregasApiService, EntregaBff, EntregasBffParams } from '@/services/entregasApiService';
export interface UseEntregasReturn { export interface UseEntregasReturn {
@ -32,6 +32,8 @@ export const useEntregas = (): UseEntregasReturn => {
const [totalEntregas, setTotalEntregas] = useState(0); const [totalEntregas, setTotalEntregas] = useState(0);
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(0); const [totalPages, setTotalPages] = useState(0);
const currentPageRef = useRef(currentPage);
currentPageRef.current = currentPage;
const listarEntregas = useCallback(async (params: EntregasBffParams = {}) => { const listarEntregas = useCallback(async (params: EntregasBffParams = {}) => {
setLoading(true); setLoading(true);
@ -40,7 +42,7 @@ export const useEntregas = (): UseEntregasReturn => {
try { try {
// Usar parâmetros fornecidos ou valores padrão // Usar parâmetros fornecidos ou valores padrão
const requestParams = { const requestParams = {
page: params.page || currentPage, page: params.page || currentPageRef.current,
limit: params.limit || DEFAULT_PAGE_SIZE, limit: params.limit || DEFAULT_PAGE_SIZE,
search: params.search, search: params.search,
status: params.status, status: params.status,
@ -67,7 +69,7 @@ export const useEntregas = (): UseEntregasReturn => {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [currentPage]); }, []);
const buscarEntregaPorId = useCallback(async (id: string): Promise<EntregaBff | null> => { const buscarEntregaPorId = useCallback(async (id: string): Promise<EntregaBff | null> => {
try { try {

View file

@ -1,4 +1,4 @@
import { useState, useCallback } from 'react'; import { useState, useCallback, useRef } from 'react';
import { Models } from 'appwrite'; import { Models } from 'appwrite';
import { produtoService, ProdutoData } from '@/services/produtoService'; import { produtoService, ProdutoData } from '@/services/produtoService';
@ -30,18 +30,21 @@ export const useProdutos = (): UseProdutosReturn => {
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [totalProdutos, setTotalProdutos] = useState(0); const [totalProdutos, setTotalProdutos] = useState(0);
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const currentPageRef = useRef(currentPage);
currentPageRef.current = currentPage;
const listarProdutos = useCallback(async (page = currentPage) => { const listarProdutos = useCallback(async (page?: number) => {
const targetPage = page ?? currentPageRef.current;
setLoading(true); setLoading(true);
setError(null); setError(null);
try { try {
const response = await produtoService.listar(page, PAGE_SIZE); const response = await produtoService.listar(targetPage, PAGE_SIZE);
if (response.success) { if (response.success) {
setProdutos(response.documents || []); setProdutos(response.documents || []);
setTotalProdutos(response.total || 0); setTotalProdutos(response.total || 0);
setCurrentPage(page); setCurrentPage(targetPage);
} else { } else {
setError(response.error || 'Erro ao carregar produtos'); setError(response.error || 'Erro ao carregar produtos');
} }
@ -50,7 +53,7 @@ export const useProdutos = (): UseProdutosReturn => {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [currentPage]); }, []);
const cadastrarProduto = useCallback(async (formData: ProdutoFormData): Promise<boolean> => { const cadastrarProduto = useCallback(async (formData: ProdutoFormData): Promise<boolean> => {
setLoading(true); setLoading(true);
@ -60,7 +63,7 @@ export const useProdutos = (): UseProdutosReturn => {
const response = await produtoService.criar(formData); const response = await produtoService.criar(formData);
if (response.success) { if (response.success) {
await listarProdutos(currentPage); await listarProdutos(currentPageRef.current);
return true; return true;
} else { } else {
setError(response.error || 'Erro ao cadastrar produto'); setError(response.error || 'Erro ao cadastrar produto');
@ -72,7 +75,7 @@ export const useProdutos = (): UseProdutosReturn => {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [currentPage, listarProdutos]); }, [listarProdutos]);
const atualizarProduto = useCallback(async (id: string, formData: ProdutoFormData): Promise<boolean> => { const atualizarProduto = useCallback(async (id: string, formData: ProdutoFormData): Promise<boolean> => {
setLoading(true); setLoading(true);
@ -82,7 +85,7 @@ export const useProdutos = (): UseProdutosReturn => {
const response = await produtoService.atualizar(id, formData); const response = await produtoService.atualizar(id, formData);
if (response.success) { if (response.success) {
await listarProdutos(currentPage); await listarProdutos(currentPageRef.current);
return true; return true;
} else { } else {
setError(response.error || 'Erro ao atualizar produto'); setError(response.error || 'Erro ao atualizar produto');
@ -94,7 +97,7 @@ export const useProdutos = (): UseProdutosReturn => {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [currentPage, listarProdutos]); }, [listarProdutos]);
const deletarProduto = useCallback(async (id: string): Promise<boolean> => { const deletarProduto = useCallback(async (id: string): Promise<boolean> => {
setLoading(true); setLoading(true);
@ -104,7 +107,7 @@ export const useProdutos = (): UseProdutosReturn => {
const response = await produtoService.deletar(id); const response = await produtoService.deletar(id);
if (response.success) { if (response.success) {
await listarProdutos(currentPage); await listarProdutos(currentPageRef.current);
return true; return true;
} else { } else {
setError(response.error || 'Erro ao deletar produto'); setError(response.error || 'Erro ao deletar produto');
@ -116,7 +119,7 @@ export const useProdutos = (): UseProdutosReturn => {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [currentPage, listarProdutos]); }, [listarProdutos]);
return { return {
produtos, produtos,