7.2 KiB
7.2 KiB
Correção do Loop Infinito no Contexto de Entregas
Problema Identificado
O aplicativo estava entrando em loop infinito devido a dependências circulares no useCallback e useEffect do DeliveriesContext.
Causa do Loop
🔄 Ciclo de Dependências
loadDeliveries (useCallback)
↓ depende de isRefreshing
refreshDeliveries (useCallback)
↓ depende de loadDeliveries
useEffect
↓ depende de loadDeliveries
loadDeliveries executa
↓ muda isRefreshing
isRefreshing muda
↓ recria loadDeliveries
loadDeliveries executa novamente
↓ LOOP INFINITO
📋 Código Problemático
// ❌ PROBLEMÁTICO - Causava loop
const loadDeliveries = useCallback(async (forceRefresh = false) => {
// ... lógica
}, [isRefreshing]) // ← Dependência que causava loop
const refreshDeliveries = useCallback(async () => {
await loadDeliveries(false)
}, [loadDeliveries]) // ← Dependência que causava loop
useEffect(() => {
loadDeliveries(false)
}, [loadDeliveries]) // ← Dependência que causava loop
Solução Implementada
1. Remoção de Dependências Circulares
// ✅ CORRIGIDO - Sem dependências problemáticas
const loadDeliveries = useCallback(async (forceRefresh = false) => {
// ... lógica
}, []) // ← Sem dependências
const refreshDeliveries = useCallback(async () => {
await loadDeliveries(false)
}, []) // ← Sem dependências
useEffect(() => {
loadDeliveries(false)
}, []) // ← Sem dependências
2. Uso de useRef para Controle de Estado
const isRefreshingRef = useRef(false) // Ref para evitar loop
const loadDeliveries = useCallback(async (forceRefresh = false) => {
// Usar ref em vez de state para verificação
if (isRefreshingRef.current && !forceRefresh) {
console.log("=== CARREGAMENTO JÁ EM ANDAMENTO - IGNORANDO ===")
return
}
try {
isRefreshingRef.current = true // Atualizar ref
setIsRefreshing(true) // Atualizar state para UI
// ... lógica de carregamento
} finally {
setLoading(false)
setIsRefreshing(false)
isRefreshingRef.current = false // Resetar ref
}
}, [])
3. Controle de Estado Sem Re-renderizações
- useRef: Para controle interno sem causar re-renderizações
- useState: Para atualizar UI quando necessário
- useCallback: Sem dependências para evitar recriação
Implementação Técnica
DeliveriesContext.tsx - Correção Completa
export const DeliveriesProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [deliveries, setDeliveries] = useState<Delivery[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [hasNoDeliveries, setHasNoDeliveries] = useState(false)
const [lastRefreshTime, setLastRefreshTime] = useState<number | null>(null)
const [isRefreshing, setIsRefreshing] = useState(false)
const isRefreshingRef = useRef(false) // ← Novo: Ref para controle interno
// Função para carregar entregas com roteamento automático
const loadDeliveries = useCallback(async (forceRefresh = false) => {
// Evitar múltiplas chamadas simultâneas usando ref
if (isRefreshingRef.current && !forceRefresh) {
console.log("=== CARREGAMENTO JÁ EM ANDAMENTO - IGNORANDO ===")
return
}
try {
isRefreshingRef.current = true // ← Atualizar ref
setIsRefreshing(true) // ← Atualizar state para UI
setLoading(true)
setError(null)
// ... lógica de carregamento e roteamento
} finally {
setLoading(false)
setIsRefreshing(false)
isRefreshingRef.current = false // ← Resetar ref
console.log("=== CARREGAMENTO FINALIZADO ===")
}
}, []) // ← Sem dependências
// Função para refresh normal (sem force)
const refreshDeliveries = useCallback(async () => {
await loadDeliveries(false)
}, []) // ← Sem dependências
// Função para force refresh (ignora se já está carregando)
const forceRefresh = useCallback(async () => {
await loadDeliveries(true)
}, []) // ← Sem dependências
// Carregar entregas na primeira vez
useEffect(() => {
loadDeliveries(false)
}, []) // ← Sem dependências
return (
<DeliveriesContext.Provider value={{
deliveries,
loading,
error,
hasNoDeliveries,
refreshDeliveries,
forceRefresh,
lastRefreshTime,
isRefreshing,
}}>
{children}
</DeliveriesContext.Provider>
)
}
Benefícios da Correção
🚫 Eliminação do Loop
- Sem dependências circulares: useCallback sem dependências problemáticas
- Controle interno: useRef para verificação sem re-renderizações
- Execução única: Carregamento acontece apenas uma vez
⚡ Performance Melhorada
- Menos re-renderizações: useRef não causa re-renderizações
- Cache estável: useCallback não recria funções desnecessariamente
- Execução eficiente: Sem chamadas duplicadas à API
🔧 Manutenibilidade
- Código limpo: Sem dependências complexas
- Debug fácil: Logs claros e previsíveis
- Comportamento estável: Sem loops inesperados
Logs de Debug - Antes e Depois
❌ Antes da Correção (Loop)
=== INICIANDO CARREGAMENTO DE ENTREGAS ===
=== CARREGAMENTO FINALIZADO ===
=== INICIANDO CARREGAMENTO DE ENTREGAS ===
=== CARREGAMENTO FINALIZADO ===
=== INICIANDO CARREGAMENTO DE ENTREGAS ===
=== CARREGAMENTO FINALIZADO ===
... (loop infinito)
✅ Depois da Correção (Normal)
=== INICIANDO CARREGAMENTO DE ENTREGAS ===
Chamando API para buscar entregas...
=== NENHUMA ENTREGA PRECISA DE ROTEAMENTO ===
Estado atualizado com as entregas ordenadas
=== CARREGAMENTO FINALIZADO ===
Cenários de Teste
1. Teste de Carregamento Inicial
# Abrir aplicação
# Verificar que carregamento acontece apenas uma vez
# Confirmar que não há loop
2. Teste de Refresh Manual
# Fazer pull-to-refresh
# Verificar que carregamento acontece apenas uma vez
# Confirmar que dados são atualizados
3. Teste de Roteamento Automático
# Configurar entregas com routing: 1
# Verificar que roteamento acontece apenas uma vez
# Confirmar que dados são atualizados
4. Teste de Navegação
# Navegar entre telas rapidamente
# Verificar que não há múltiplas chamadas à API
# Confirmar que dados são consistentes
Compatibilidade
✅ Plataformas
- Android: Totalmente compatível
- iOS: Totalmente compatível
- Expo SDK 53: Compatível
✅ React Hooks
- useCallback: Sem dependências problemáticas
- useEffect: Execução única
- useRef: Controle interno sem re-renderizações
- useState: Apenas para UI
✅ Performance
- Sem loops: Execução controlada
- Cache eficiente: Funções estáveis
- Menos re-renderizações: useRef para controle interno
Próximos Passos
🔮 Melhorias Futuras
- Debounce: Evitar múltiplas chamadas em sequência rápida
- Cache inteligente: Salvar dados localmente
- Retry automático: Tentar novamente em caso de erro
- Loading states: Estados de carregamento mais granulares
- Error boundaries: Tratamento de erro mais robusto