mirror of
https://github.com/penpot/penpot.git
synced 2026-08-13 16:28:59 +00:00
* 🐛 Filter ignorable exceptions in error-boundary onError callback The global uncaught-error-handler already skips NotFoundError/removeChild and other harmless errors, but react-error-boundary's onError callback fires independently of the window.onerror pipeline. This means the error boundary was still logging these errors and setting last-exception, causing them to continue appearing in error reports despite being non-actionable. Add the is-ignorable-exception? check to the error-boundary* onError so harmless errors are silently ignored, matching the behavior of the global handler. * 🐛 Fix dangerouslySetInnerHTML anti-pattern in context-notification The previous code used dangerouslySetInnerHTML on the same element that could also contain React children. This is a React anti-pattern that can cause reconciliation mismatches and lead to removeChild DOMExceptions. Refactor to use two separate element branches: one for raw HTML injection and one for normal React children with links.
68 lines
2.8 KiB
Clojure
68 lines
2.8 KiB
Clojure
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
;;
|
|
;; Copyright (c) KALEIDOS INC Sucursal en España SL
|
|
|
|
(ns app.main.ui.error-boundary
|
|
"React error boundary components"
|
|
(:require
|
|
["react-error-boundary" :as reb]
|
|
[app.common.exceptions :as ex]
|
|
[app.config :as cf]
|
|
[app.main.errors :as errors]
|
|
[app.main.refs :as refs]
|
|
[goog.functions :as gfn]
|
|
[rumext.v2 :as mf]))
|
|
|
|
(mf/defc error-boundary*
|
|
[{:keys [fallback children]}]
|
|
(let [fallback-wrapper
|
|
(mf/with-memo [fallback]
|
|
(mf/fnc fallback-wrapper*
|
|
[{:keys [error reset-error-boundary]}]
|
|
(let [route (mf/deref refs/route)
|
|
data (errors/exception->error-data error)]
|
|
[:> fallback {:data data
|
|
:route route
|
|
:on-reset reset-error-boundary}])))
|
|
|
|
on-error
|
|
(mf/with-memo []
|
|
;; NOTE: The debounce is necessary just for simplicity,
|
|
;; becuase for some reasons the error is reported twice in a
|
|
;; very small amount of time, so we debounce for 100ms for
|
|
;; avoid duplicate and redundant reports
|
|
(gfn/debounce (fn [error info]
|
|
;; If the error is a stale-asset error (cross-build
|
|
;; module mismatch), force a hard page reload instead
|
|
;; of showing the error page to the user.
|
|
(cond
|
|
(errors/stale-asset-error? error)
|
|
(cf/throttled-reload :reason (ex-message error))
|
|
|
|
;; If the error is known to be harmless (browser
|
|
;; extensions, React DOM conflicts, etc.), ignore it
|
|
;; silently — the global uncaught-error-handler
|
|
;; already does this, but react-error-boundary's
|
|
;; onError fires independently of the window.onerror
|
|
;; pipeline, so we must also filter here.
|
|
(errors/is-ignorable-exception? error)
|
|
nil
|
|
|
|
:else
|
|
(do
|
|
(set! errors/last-exception error)
|
|
(ex/print-throwable error)
|
|
(js/console.error
|
|
"Component trace: \n"
|
|
(unchecked-get info "componentStack")
|
|
"\n"
|
|
error))))
|
|
100))]
|
|
|
|
[:> reb/ErrorBoundary
|
|
{:FallbackComponent fallback-wrapper
|
|
:onError on-error}
|
|
children]))
|