import React , { useState , useCallback , useMemo , useRef } from 'react' ; interface ComponentHolder { value : React . ReactElement ; orderNumber : number | null ; } export interface AreaContextValue { addComponent : ( areaId : string | number , component : ComponentHolder ) => void ; removeComponent : ( areaId : string | number , component : ComponentHolder ) => void ; updateComponent : ( areaId : string | number , component : ComponentHolder ) => void ; getComponents : ( areaId : string ) => React . ReactElement [ ] | null ; orderNumberRef : { current : number ; } ; } export const AreaContext = React . createContext < AreaContextValue > ( { addComponent : ( areaId , component ) => { } , removeComponent : ( areaId , component ) => { } , updateComponent : ( areaId , component ) => { } , getComponents : ( areaId ) => null , orderNumberRef : { current : 0 } , } ) ; export function AreaProvider ( props ) { const [ components , setComponents ] = useState < { [ key ] : Set < ComponentHolder > ; } > ( { } ) ; const addComponent = useCallback ( ( areaId , component ) => { setComponents ( components => { const existingComponents = components [ areaId ] || new Set ( ) ; if ( existingComponents . has ( component ) ) { console . log ( 'component has already been registererd' ) ; return components ; } existingComponents . add ( component ) ; return { ... components , [ areaId ] : existingComponents , } ; } ) ; } , [ ] ) ; const removeComponent = useCallback ( ( areaId , ref ) => { setComponents ( components => { const existingComponents = components [ areaId ] ; if ( ! existingComponents ) { return components ; } existingComponents . delete ( ref ) ; existingComponents . forEach ( component => { component . orderNumber = null ; } ) ; return { ... components } ; } ) ; } , [ ] ) ; const updateComponent = useCallback ( ( areaId , _component ) => { setComponents ( components => { const existingComponents = components [ areaId ] ; if ( ! existingComponents ) { return components ; } return { ... components } ; } ) ; } , [ ] ) ; const getComponents = useCallback ( areaId => { if ( ! ( areaId in components ) ) { return null ; } const areaComponents = Array . from ( components [ areaId ] ) ; areaComponents . sort ( ( a , b ) => a . orderNumber ! - b . orderNumber ! ) ; return areaComponents . map ( component => component . value ) ; } , [ components ] ) ; const orderNumberRef = useRef ( 0 ) ; const value = useMemo ( ( ) => ( { addComponent , getComponents , removeComponent , updateComponent , orderNumberRef , } ) , [ addComponent , getComponents , removeComponent , updateComponent ] ) ; return < AreaContext . Provider { ... props } value = { value } / > ; }