Skip to content

Instantly share code, notes, and snippets.

@threepointone
Created December 8, 2020 00:16
Show Gist options
  • Select an option

  • Save threepointone/e73a87f7bbbebc78cf71744469ec5a15 to your computer and use it in GitHub Desktop.

Select an option

Save threepointone/e73a87f7bbbebc78cf71744469ec5a15 to your computer and use it in GitHub Desktop.

Revisions

  1. threepointone created this gist Dec 8, 2020.
    61 changes: 61 additions & 0 deletions iframe.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,61 @@
    import { Suspense, useLayoutEffect, useRef, useState } from 'react';

    type IFrameProps = React.ComponentPropsWithRef<'iframe'> & {
    fallback?: JSX.Element;
    };

    export function IFrame(props: IFrameProps) {
    const { fallback, ...rest } = props;

    return (
    // @ts-expect-error
    <Suspense unstable_avoidThisFallback={fallback || 'loading...'}>
    <IFrameImplementation {...rest} />
    </Suspense>
    );
    }

    function IFrameImplementation(props: React.ComponentPropsWithRef<'iframe'>) {
    const awaiter = useRef<null | {
    promise: null | Promise<void>;
    resolve: () => void;
    reject: () => void;
    }>(null);
    const [_, triggerLoad] = useState(false)
    if (awaiter.current?.promise) {
    console.log('suspend')
    throw awaiter.current.promise;
    }
    useLayoutEffect(() => {
    if (awaiter.current === null) {
    // @ts-ignore
    awaiter.current = {}
    // @ts-ignore
    awaiter.current.promise = new Promise<void>((resolve, reject) => {
    Object.assign(awaiter.current, { resolve, reject });
    });
    triggerLoad(true)
    }
    }, []);
    const { title } = props;
    return (
    <iframe
    {...props}
    title={title}
    onLoad={(e) => {
    // @ts-ignore
    awaiter.current.promise = null;
    awaiter.current?.resolve();
    props.onLoad?.(e);
    }}
    onError={(err) => {
    // @ts-ignore
    awaiter.current.promise = null;
    awaiter.current?.reject();
    props.onError?.(err);
    }}
    />
    );
    }

    export default IFrame;