252 lines
7.2 KiB
Markdown
252 lines
7.2 KiB
Markdown
# 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**
|
|
```typescript
|
|
// ❌ 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**
|
|
```typescript
|
|
// ✅ 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**
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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**
|
|
```bash
|
|
# Abrir aplicação
|
|
# Verificar que carregamento acontece apenas uma vez
|
|
# Confirmar que não há loop
|
|
```
|
|
|
|
### 2. **Teste de Refresh Manual**
|
|
```bash
|
|
# Fazer pull-to-refresh
|
|
# Verificar que carregamento acontece apenas uma vez
|
|
# Confirmar que dados são atualizados
|
|
```
|
|
|
|
### 3. **Teste de Roteamento Automático**
|
|
```bash
|
|
# Configurar entregas com routing: 1
|
|
# Verificar que roteamento acontece apenas uma vez
|
|
# Confirmar que dados são atualizados
|
|
```
|
|
|
|
### 4. **Teste de Navegação**
|
|
```bash
|
|
# 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 |