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 comfallback(com uma limitação importante, veja abaixo)catchAsync: Wrapper para funções assíncronas com tratamento de errosafeRender: Executa uma view e devolve um fallback se ela lançar (cliente e SSR). É a forma de proteger uma subárvoresetupGlobalErrorHandler: Handler global para erros não capturados
ErrorBoundary
Section titled “ErrorBoundary”O ErrorBoundary recebe fallback, onError e children.
Props do ErrorBoundary
Section titled “Props do ErrorBoundary”type ErrorBoundaryProps = { /** Obrigatório: UI de fallback quando um erro é capturado */ fallback: (error: Error) => Child
/** Callback opcional chamado quando um erro é capturado */ onError?: (error: Error, errorInfo: { componentStack?: string }) => void
/** Children a serem renderizados */ children: Child}Protegendo seções com safeRender
Section titled “Protegendo seções com safeRender”import { html, safeRender } from '@_bashell/slash'
// Componentes de exemplofunction UserWidget() { return html`<section>Usuário</section>`}function StatsWidget(): never { throw new Error('Falha ao montar as estatísticas')}
const widgetError = (message: string) => (error: Error) => html` <div class="widget-error"> <p>${message}</p> <small>${error.message}</small> </div>`
function Dashboard() { return html` <div class="dashboard"> <h1>Dashboard</h1>
${safeRender(() => html`<${UserWidget} />`, widgetError('Usuário indisponível.'))} ${safeRender(() => html`<${StatsWidget} />`, widgetError('Erro ao carregar estatísticas.'))} </div> `}Cada safeRender é independente: se um widget falhar, os outros continuam funcionando. safeRender registra o erro com console.error e devolve o resultado de fallback.
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, render, catchAsync } from '@_bashell/slash'
// State e função segura ficam fora do componente: o componente re-executa// quando o que ele lê muda, e criar/disparar isso no corpo repetiria o fetchconst userData = createState<any>(null)let userId = 1
// Criar função segura que captura errosconst [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) })
const loadUser = async () => { const data = await fetchUser() if (data) userData.set(data)}
function UserProfile() { // getError() e userData.get() inscrevem o componente const error = getError() const user = userData.get()
if (error) { return html` <div class="error"> <p>Erro: ${error.message}</p> <button onClick=${loadUser}>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> `}
// Monte como <${UserProfile} /> e dispare o carregamento fora do componenterender(html`<${UserProfile} />`, '#app')loadUser()Exemplo: Múltiplas operações assíncronas
Section titled “Exemplo: Múltiplas operações assíncronas”import { html, createState, render, catchAsync } from '@_bashell/slash'
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 (fora do componente)const loadAll = () => Promise.all([fetchData1(), fetchData2(), fetchData3()]).then( ([data1, data2, data3]) => { data.set({ data1, data2, data3 }) } )
function DataDashboard() { // Estas leituras inscrevem o componente const error1 = getError1() const error2 = getError2() const error3 = getError3() const current = data.get()
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>`}
${current && html`<pre>${JSON.stringify(current, null, 2)}</pre>`} </div> `}
render(html`<${DataDashboard} />`, '#app')loadAll()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, serializeStateForScript } from '@_bashell/slash/ssr'import { safeRender } from '@_bashell/slash'
// No servidor (Express). `App` é um componente feito com htmlStringapp.get('*', (req, res) => { const body = safeRender( () => { const { html, state } = renderToString(() => App({ url: req.url })) return `<div id="root">${html}</div> <script id="__SLASH_STATE__" type="application/json">${serializeStateForScript(state)}</script>` }, (error) => { console.error('Erro SSR:', error) return `<div class="error">Erro ao renderizar página</div>` } )
// O documento é um template literal comum (não use htmlString com <!DOCTYPE>) res.send(`<!DOCTYPE html><html> <body> ${body} <script src="/client.js"></script> </body></html>`)})Uso em componentes
Section titled “Uso em componentes”import { html } from '@_bashell/slash'import { safeRender } from '@_bashell/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 '@_bashell/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, safeRender, setupGlobalErrorHandler } from '@_bashell/slash'
// 1. Handler global para o que escapa de tudo (handlers, promises, erros de runtime)setupGlobalErrorHandler((error, source) => { console.error(`[Global] Erro ${source}:`, error) // envie para o seu serviço de logging})
function App() { return html`<main><h1>Minha aplicação</h1></main>`}
// 2. Proteja a montagem da raiz com safeRenderconst root = safeRender( () => html`<${App} />`, (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> `)
render(root, '#root')Best practices
Section titled “Best practices”1. Isole seções com safeRender
Section titled “1. Isole seções com safeRender”// Ruim: uma única proteção para tudo (um erro derruba a página inteira)const page = safeRender( () => html`<div><${Header} /><${Sidebar} /><${MainContent} /><${Footer} /></div>`, pageFallback)
// Bom: cada seção crítica isoladaconst page = html` <div> <${Header} /> ${safeRender(() => html`<${Sidebar} />`, sidebarFallback)} ${safeRender(() => html`<${MainContent} />`, contentFallback)} <${Footer} /> </div>`2. Forneça UX útil nos fallbacks
Section titled “2. Forneça UX útil nos fallbacks”// ❌ Ruim: Mensagem genéricaconst fallback = (error: Error) => html`<div>Erro</div>`
// ✅ Bom: Informação útil + açãoconst betterFallback = (error: 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 { html, safeRender } from '@_bashell/slash'
const view = safeRender( () => html`<${Content} />`, (error) => { // Analytics / Sentry / log interno trackError(error) if (typeof Sentry !== 'undefined') { Sentry.captureException(error) } return html`<p>Algo deu errado.</p>` })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: quem chama decide (ex.: catchAsync ou try/catch) }}5. Use safeRender em SSR
Section titled “5. Use safeRender em SSR”Veja o exemplo completo em Uso em SSR: o documento é montado com template literal comum, e o fallback devolve uma string HTML montada à mão, o que só funciona porque ela entra num template literal comum. Dentro de html/htmlString, uma string é sempre texto: devolva um template (ou unsafeHtml(...) para marcação confiável).
Comparação com outros frameworks
Section titled “Comparação com outros frameworks”| Framework | Error Boundary | Async Errors | Global Handler |
|---|---|---|---|
| Slash | ⚠️ ErrorBoundary (limitado) + safeRender | ✅ 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