entregas_app/docs/CORRECAO_LOOP_CONTEXTO.md

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