1+ import { useRef , memo } from "react" ;
12import { createPortal } from "react-dom" ;
23import { useGridStackContext } from "./grid-stack-context" ;
34import { useGridStackRenderContext } from "./grid-stack-render-context" ;
@@ -10,12 +11,24 @@ export interface ComponentDataType<T = object> {
1011 props : T ;
1112}
1213
14+ type ParsedComponentData = ComponentDataType & {
15+ error : unknown ;
16+ metaHash : string ;
17+ } ;
18+
1319// eslint-disable-next-line @typescript-eslint/no-explicit-any
1420export type ComponentMap = Record < string , ComponentType < any > > ;
1521
1622function parseWeightMetaToComponentData (
17- meta : GridStackWidget
23+ meta : GridStackWidget ,
24+ cache : Map < string , ParsedComponentData >
1825) : ComponentDataType & { error : unknown } {
26+ const cacheKey = meta . id ?? "" ;
27+ const metaHash = meta . content ?? "" ;
28+ const cached = cache . get ( cacheKey ) ;
29+ // Ensure componentData is immutable between renders
30+ if ( cached && cached . metaHash === metaHash ) return cached ;
31+
1932 let error = null ;
2033 let name = "" ;
2134 let props = { } ;
@@ -31,37 +44,71 @@ function parseWeightMetaToComponentData(
3144 } catch ( e ) {
3245 error = e ;
3346 }
34- return {
47+ const parsed : ParsedComponentData = {
3548 name,
3649 props,
3750 error,
51+ metaHash,
3852 } ;
53+ cache . set ( cacheKey , parsed ) ;
54+ return parsed ;
3955}
4056
57+ type WidgetMemoProps = {
58+ id : string ;
59+ componentData : ComponentDataType ;
60+ widgetContainer : HTMLElement ;
61+ WidgetComponent : ComponentType < unknown > ;
62+ } ;
63+
64+ const WidgetMemo = memo (
65+ ( {
66+ id,
67+ componentData,
68+ widgetContainer,
69+ WidgetComponent,
70+ } : WidgetMemoProps ) => {
71+ return (
72+ < GridStackWidgetContext . Provider value = { { widget : { id } } } >
73+ { createPortal (
74+ < WidgetComponent { ...componentData . props } /> ,
75+ widgetContainer
76+ ) }
77+ </ GridStackWidgetContext . Provider >
78+ ) ;
79+ }
80+ ) ;
81+ WidgetMemo . displayName = "WidgetMemo" ;
82+
4183export function GridStackRender ( props : { componentMap : ComponentMap } ) {
4284 const { _rawWidgetMetaMap } = useGridStackContext ( ) ;
4385 const { getWidgetContainer } = useGridStackRenderContext ( ) ;
86+ const parsedCache = useRef < Map < string , ParsedComponentData > > ( new Map ( ) ) ;
4487
4588 return (
4689 < >
4790 { Array . from ( _rawWidgetMetaMap . value . entries ( ) ) . map ( ( [ id , meta ] ) => {
48- const componentData = parseWeightMetaToComponentData ( meta ) ;
91+ const componentData = parseWeightMetaToComponentData (
92+ meta ,
93+ parsedCache . current
94+ ) ;
4995
5096 const WidgetComponent = props . componentMap [ componentData . name ] ;
5197
5298 const widgetContainer = getWidgetContainer ( id ) ;
5399
54100 if ( ! widgetContainer ) {
55- throw new Error ( `Widget container not found for id: ${ id } ` ) ;
101+ return null ;
56102 }
57103
58104 return (
59- < GridStackWidgetContext . Provider key = { id } value = { { widget : { id } } } >
60- { createPortal (
61- < WidgetComponent { ...componentData . props } /> ,
62- widgetContainer
63- ) }
64- </ GridStackWidgetContext . Provider >
105+ < WidgetMemo
106+ id = { id }
107+ key = { id }
108+ componentData = { componentData }
109+ widgetContainer = { widgetContainer }
110+ WidgetComponent = { WidgetComponent }
111+ />
65112 ) ;
66113 } ) }
67114 </ >
0 commit comments