import React from "react" ; import { isEqual } from "lodash" ; import Spinner from "@/components/Spinner" ; export const isComponentClass = ( component ) : boolean => { if ( ! component ) return false ; const proto = Object . getPrototypeOf ( component ) ; if ( proto === React . Component || proto === Function . prototype ) return true ; return isComponentClass ( proto ) ; } ; interface PromiseRenderProps < T , K > { ok : T ; error : K ; promise : Promise < boolean > ; } interface PromiseRenderState { component : React . ComponentClass | React . FunctionComponent ; } export default class PromiseRender < T , K > extends React . Component < PromiseRenderProps < T , K > , PromiseRenderState > { state : PromiseRenderState = { component : ( ) => null , } ; componentDidMount ( ) { this . setRenderComponent ( this . props ) ; } shouldComponentUpdate = ( nextProps , nextState ) => { const { component } = this . state ; if ( ! isEqual ( nextProps , this . props ) ) { this . setRenderComponent ( nextProps ) ; } return nextState . component !== component ; } ; setRenderComponent ( props ) { const ok = this . checkIsInstantiation ( props . ok ) ; const error = this . checkIsInstantiation ( props . error ) ; props . promise . then ( ( ) => { this . setState ( { component : ok , } ) ; return true ; } ) . catch ( ( ) => { this . setState ( { component : error , } ) ; } ) ; } checkIsInstantiation = ( target ) : React . FunctionComponent => { if ( isComponentClass ( target ) ) { const Target = target as React . ComponentClass ; return ( props ) => < Target { ... props } / > ; } if ( React . isValidElement ( target ) ) { return ( props ) => React . cloneElement ( target , props ) ; } return ( ) => target as React . ReactNode & null ; } ; render ( ) { const { component : Component } = this . state ; const { ok , error , promise , ... rest } = this . props ; return Component ? < Component { ... rest } / > : < Spinner />; } }