Skip to content

Instantly share code, notes, and snippets.

@enriquebeta6
Last active September 5, 2022 20:43
Show Gist options
  • Save enriquebeta6/175612ad7d70906dc15c6bb393e0f738 to your computer and use it in GitHub Desktop.
Save enriquebeta6/175612ad7d70906dc15c6bb393e0f738 to your computer and use it in GitHub Desktop.
// Depedencies
import React from 'react'
import { createPortal } from 'react-dom'
// Components
import ErrorBoundary from './ErrorBoundary'
interface SafePortalProps {
rootSelector: string
applyCleanup?: boolean
}
interface SafePortalState {
container: Element | null
}
export default class SafePortal extends React.Component<
SafePortalProps,
SafePortalState
> {
constructor(props: SafePortalProps) {
super(props)
this.state = {
container: null,
}
}
componentDidMount() {
const { rootSelector, applyCleanup } = this.props
const newContainer = document.querySelector(rootSelector)
if (newContainer && applyCleanup) {
newContainer.innerHTML = ''
}
this.setState({
container: newContainer,
})
}
render() {
const { children } = this.props
const { container } = this.state
if (!container) return null
return <ErrorBoundary>{createPortal(children, container)}</ErrorBoundary>
}
}
// Dependencies
import React from 'react'
interface ErrorBoundaryProps {
fallback?: React.ReactNode
children: React.ReactNode
}
interface ErrorBoundaryState {
error: boolean
}
export default class ErrorBoundary extends React.Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
constructor(props: ErrorBoundaryProps) {
super(props)
this.state = {
error: false,
}
}
componentDidCatch(error: unknown, errorInfo: unknown) {
// This console can be changed to an external tool like Sentry
console.error({
error,
errorInfo,
})
this.setState({
error: true,
})
}
render() {
const { error } = this.state
const { children, fallback = null } = this.props
return error ? fallback : children
}
}
// Depedencies
import React, { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
// Components
import ErrorBoundary from './ErrorBoundary';
interface SafePortalProps {
rootSelector: string;
applyCleanup?: boolean;
}
const SafePortal: React.FC<SafePortalProps> = ({
children,
rootSelector,
applyCleanup = true,
}) => {
const [container, setContainer] = useState<HTMLElement>();
useEffect(() => {
const element = document.querySelector(rootSelector);
if (!element) return;
if (applyCleanup) {
element.innerHTML = '';
}
setContainer(element as HTMLElement);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (!container) return null;
return <ErrorBoundary>{createPortal(children, container)}</ErrorBoundary>;
};
export default SafePortal;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment