Error Handling
Tratamento de erros no Slash
Section titled “Tratamento de erros no Slash”O Slash fornece ferramentas para capturar e tratar erros tanto síncronos quanto assíncronos, em client-side e server-side rendering (SSR).
Ferramentas disponíveis
Section titled “Ferramentas disponíveis”ErrorBoundary: Componente para capturar erros durante renderizaçãocatchAsync: Wrapper para funções assíncronas com tratamento de errosafeRender: Renderização segura para SSRsetupGlobalErrorHandler: Handler global para erros não capturados
ErrorBoundary
Section titled “ErrorBoundary”O ErrorBoundary é um componente que captura erros de renderização em seus children e exibe um fallback UI.
Uso básico
Section titled “Uso básico”import { html } from '@ezbug/slash'import { ErrorBoundary } from '@ezbug/slash'
function App() { return html` <div> <h1>Minha Aplicação</h1>
<${ErrorBoundary} fallback=${(error) => html` <div class="error-container"> <h2>Algo deu errado!</h2> <p>${error.message}</p> </div> `} onError=${(error, errorInfo) => { console.error('Erro capturado:', error) // Enviar para serviço de logging (ex: Sentry) }} > <${ProblematicComponent} /> </${ErrorBoundary}> </div> `}Props do ErrorBoundary
Section titled “Props do ErrorBoundary”type ErrorBoundaryProps = { /** Função que renderiza UI de fallback quando há erro */ fallback: (error: Error) => Child
/** Callback opcional chamado quando erro ocorre */ onError?: (error: Error, errorInfo: { componentStack?: string }) => void
/** Children a serem renderizados */ children: Child}Exemplo: Proteção de seções críticas
Section titled “Exemplo: Proteção de seções críticas”import { html } from '@ezbug/slash'import { ErrorBoundary } from '@ezbug/slash'
function Dashboard() { return html` <div class="dashboard"> <header> <h1>Dashboard</h1> </header>
<!-- Seção 1: Protegida --> <${ErrorBoundary} fallback=${(error) => html` <div class="widget-error"> <p>Não foi possível carregar os dados do usuário.</p> <button onClick=${() => window.location.reload()}> Tentar novamente </button> </div> `} > <${UserWidget} /> </${ErrorBoundary}>
<!-- Seção 2: Protegida --> <${ErrorBoundary} fallback=${(error) => html` <div class="widget-error"> <p>Erro ao carregar estatísticas.</p> </div> `} > <${StatsWidget} /> </${ErrorBoundary}>
<!-- Seção 3: Protegida --> <${ErrorBoundary} fallback=${(error) => html` <div class="widget-error"> <p>Gráfico indisponível no momento.</p> </div> `} > <${ChartWidget} /> </${ErrorBoundary}> </div> `}Exemplo: Integração com serviços de logging
Section titled “Exemplo: Integração com serviços de logging”import { html } from '@ezbug/slash'import { ErrorBoundary } from '@ezbug/slash'
// Integração com Sentry (exemplo)function logErrorToSentry(error: Error, errorInfo: any) { if (typeof Sentry !== 'undefined') { Sentry.captureException(error, { contexts: { react: errorInfo } }) }}
function App() { return html` <${ErrorBoundary} fallback=${(error) => html` <div class="error-page"> <h1>Oops!</h1> <p>Algo inesperado aconteceu.</p> <details> <summary>Detalhes técnicos</summary> <pre>${error.stack}</pre> </details> </div> `} onError=${logErrorToSentry} > <${Router} router=${router} /> </${ErrorBoundary}> `}catchAsync
Section titled “catchAsync”catchAsync é um wrapper para funções assíncronas que captura erros automaticamente:
function catchAsync<T>( fn: () => Promise<T>, onError?: (error: Error) => void): [ safeFn: () => Promise<T | null>, getError: () => Error | null]Uso básico
Section titled “Uso básico”import { html, createState } from '@ezbug/slash'import { catchAsync } from '@ezbug/slash'
function UserProfile({ userId }: { userId: number }) { const userData = createState<any>(null)
// Criar função segura que captura erros const [fetchUser, getError] = catchAsync( async () => { const res = await fetch(`/api/users/${userId}`) if (!res.ok) throw new Error('Falha ao carregar usuário') return res.json() }, (error) => { console.error('Erro ao buscar usuário:', error) } )
// Executar fetch fetchUser().then((data) => { if (data) userData.set(data) })
const error = getError() const user = userData.get()
if (error) { return html` <div class="error"> <p>Erro: ${error.message}</p> <button onClick=${() => fetchUser()}>Tentar novamente</button> </div> ` }
if (!user) { return html`<p>Carregando...</p>` }
return html` <div class="user-profile"> <h2>${user.name}</h2> <p>${user.email}</p> </div> `}Exemplo: Múltiplas operações assíncronas
Section titled “Exemplo: Múltiplas operações assíncronas”import { html, createState } from '@ezbug/slash'import { catchAsync } from '@ezbug/slash'
function DataDashboard() { const data = createState<any>(null)
const [fetchData1, getError1] = catchAsync(async () => { const res = await fetch('/api/data1') return res.json() })
const [fetchData2, getError2] = catchAsync(async () => { const res = await fetch('/api/data2') return res.json() })
const [fetchData3, getError3] = catchAsync(async () => { const res = await fetch('/api/data3') return res.json() })
// Executar todas as requisições Promise.all([fetchData1(), fetchData2(), fetchData3()]).then( ([data1, data2, data3]) => { data.set({ data1, data2, data3 }) } )
const error1 = getError1() const error2 = getError2() const error3 = getError3()
return html` <div> ${error1 && html`<p class="error">Erro ao carregar dados 1</p>`} ${error2 && html`<p class="error">Erro ao carregar dados 2</p>`} ${error3 && html`<p class="error">Erro ao carregar dados 3</p>`}
${data.get() && html`<pre>${JSON.stringify(data.get(), null, 2)}</pre>`} </div> `}safeRender
Section titled “safeRender”safeRender é um wrapper para renderização segura, especialmente útil para SSR:
function safeRender( view: () => Child, fallback: (error: Error) => Child): ChildUso em SSR
Section titled “Uso em SSR”import { renderToString, safeRender } from '@ezbug/slash'
// No servidorapp.get('*', (req, res) => { const html = safeRender( () => renderToString(html`<${App} url=${req.url} />`), (error) => { console.error('Erro SSR:', error) return `<div class="error">Erro ao renderizar página</div>` } )
res.send(` <!DOCTYPE html> <html> <body> <div id="root">${html}</div> <script src="/client.js"></script> </body> </html> `)})Uso em componentes
Section titled “Uso em componentes”import { html } from '@ezbug/slash'import { safeRender } from '@ezbug/slash'
function DangerousComponent() { return safeRender( () => { // Código que pode lançar erro const data = JSON.parse(someUntrustedString) return html`<div>${data.value}</div>` }, (error) => html` <div class="error"> <p>Erro ao processar dados</p> <pre>${error.message}</pre> </div> ` )}setupGlobalErrorHandler
Section titled “setupGlobalErrorHandler”Configura um handler global para capturar todos os erros não tratados:
function setupGlobalErrorHandler( onError: (error: Error, source: 'render' | 'runtime') => void): voidUso básico
Section titled “Uso básico”import { setupGlobalErrorHandler } from '@ezbug/slash'
// Configurar no início da aplicação (client-side)setupGlobalErrorHandler((error, source) => { console.error(`Erro ${source}:`, error)
// Enviar para serviço de monitoramento if (typeof Sentry !== 'undefined') { Sentry.captureException(error, { tags: { source } }) }
// Exibir toast/notificação para o usuário showErrorNotification('Algo deu errado. Por favor, recarregue a página.')})Exemplo: Sistema completo de error handling
Section titled “Exemplo: Sistema completo de error handling”import { html, render } from '@ezbug/slash'import { ErrorBoundary, setupGlobalErrorHandler } from '@ezbug/slash'
// 1. Setup global error handlersetupGlobalErrorHandler((error, source) => { console.error(`[Global] Erro ${source}:`, error)
// Log para serviço externo logToExternalService({ error: error.message, stack: error.stack, source, timestamp: new Date().toISOString() })})
// 2. App com ErrorBoundaryfunction App() { return html` <${ErrorBoundary} fallback=${(error) => html` <div class="app-error"> <h1>Erro Fatal</h1> <p>A aplicação encontrou um erro inesperado.</p> <button onClick=${() => window.location.reload()}> Recarregar página </button> <details> <summary>Detalhes</summary> <pre>${error.stack}</pre> </details> </div> `} onError=${(error) => { console.error('[App] Erro capturado:', error) }} > <${Router} router=${router} /> </${ErrorBoundary}> `}
// 3. Renderrender(html`<${App} />`, document.getElementById('root')!)Best practices
Section titled “Best practices”1. Use ErrorBoundary estrategicamente
Section titled “1. Use ErrorBoundary estrategicamente”// ❌ Ruim: Um único ErrorBoundary para tudofunction App() { return html` <${ErrorBoundary} fallback=${fallback}> <${Header} /> <${Sidebar} /> <${MainContent} /> <${Footer} /> </${ErrorBoundary}> `}
// ✅ Bom: ErrorBoundary isolando seções críticasfunction App() { return html` <div> <${Header} />
<${ErrorBoundary} fallback=${sidebarFallback}> <${Sidebar} /> </${ErrorBoundary}>
<${ErrorBoundary} fallback=${contentFallback}> <${MainContent} /> </${ErrorBoundary}>
<${Footer} /> </div> `}2. Forneça UX útil nos fallbacks
Section titled “2. Forneça UX útil nos fallbacks”// ❌ Ruim: Mensagem genéricafallback: (error) => html`<div>Erro</div>`
// ✅ Bom: Informação útil + açãofallback: (error) => html` <div class="error-widget"> <p>Não foi possível carregar os dados.</p> <button onClick=${retry}>Tentar novamente</button> <a href="/help">Precisa de ajuda?</a> </div>`3. Log erros para monitoramento
Section titled “3. Log erros para monitoramento”import { ErrorBoundary } from '@ezbug/slash'
function App() { return html` <${ErrorBoundary} fallback=${fallback} onError=${(error) => { // Analytics trackError(error)
// Sentry/Bugsnag/etc if (typeof Sentry !== 'undefined') { Sentry.captureException(error) }
// Log interno console.error('Error:', error) }} > <${Content} /> </${ErrorBoundary}> `}4. Trate erros assíncronos explicitamente
Section titled “4. Trate erros assíncronos explicitamente”// ❌ Ruim: Erro silenciosoasync function loadData() { const res = await fetch('/api/data') const data = await res.json() return data}
// ✅ Bom: Tratamento explícitoasync function loadData() { try { const res = await fetch('/api/data') if (!res.ok) { throw new Error(`HTTP ${res.status}: ${res.statusText}`) } return await res.json() } catch (error) { console.error('Erro ao carregar dados:', error) throw error // Re-throw para que ErrorBoundary possa capturar }}5. Use safeRender em SSR
Section titled “5. Use safeRender em SSR”import { renderToString, safeRender } from '@ezbug/slash'
// No servidorapp.get('*', (req, res) => { const html = safeRender( () => renderToString(html`<${App} />`), (error) => { // Log no servidor console.error('SSR Error:', error)
// Retornar fallback UI return `<div>Erro ao renderizar página</div>` } )
res.send(wrapHtml(html))})Comparação com outros frameworks
Section titled “Comparação com outros frameworks”| Framework | Error Boundary | Async Errors | Global Handler |
|---|---|---|---|
| Slash | ✅ ErrorBoundary | ✅ catchAsync | ✅ setupGlobalErrorHandler |
| React | ✅ Class-based | ❌ Manual try/catch | ✅ window.onerror |
| Vue | ✅ errorCaptured | ❌ Manual | ✅ app.config.errorHandler |
| Solid | ✅ ErrorBoundary | ❌ Manual | ✅ onError |
Próximos passos
Section titled “Próximos passos”- SSR - Error handling no servidor
- Componentes - Criando componentes resilientes
- Estado - Gerenciamento de estado com tratamento de erro