@@ -3,6 +3,7 @@ import { useDataQuery } from '@dhis2/app-service-data'
33import postRobot from 'post-robot'
44import React , {
55 ReactEventHandler ,
6+ useCallback ,
67 useContext ,
78 useEffect ,
89 useMemo ,
@@ -65,8 +66,7 @@ export const Plugin = ({
6566
6667 const [ communicationReceived , setCommunicationReceived ] =
6768 useState < boolean > ( false )
68- const [ prevCommunicationReceived , setPrevCommunicationReceived ] =
69- useState < boolean > ( false )
69+ const [ , setPrevCommunicationReceived ] = useState < boolean > ( false )
7070
7171 const [ inErrorState , setInErrorState ] = useState < boolean > ( false )
7272 const [ pluginHeight , setPluginHeight ] = useState < string | number > ( '150px' )
@@ -93,18 +93,25 @@ export const Plugin = ({
9393 /* eslint-enable react-hooks/exhaustive-deps */
9494 )
9595
96- useEffect ( ( ) => {
97- setCommunicationReceived ( false )
98- } , [ pluginEntryPoint ] )
96+ const propsFromParentListenerRef = useRef < any > ( )
9997
100- useEffect ( ( ) => {
98+ const setUpCommunication = useCallback ( ( ) => {
99+ // use function arg to set state here to avoid requiring dependency
100+ let prevCommunication
101+ setPrevCommunicationReceived ( ( prevValue ) => {
102+ prevCommunication = prevValue
103+ return communicationReceived
104+ } )
101105 // if communicationReceived switches from false to true, the props have been sent
102- const prevCommunication = prevCommunicationReceived
103- setPrevCommunicationReceived ( communicationReceived )
106+ // the following check avoids sending the props twice in a row
104107 if ( prevCommunication === false && communicationReceived === true ) {
105108 return
106109 }
107110
111+ if ( inErrorState ) {
112+ return
113+ }
114+
108115 if ( iframeRef ?. current ) {
109116 const iframeProps = {
110117 ...memoizedPropsToPass ,
@@ -116,25 +123,30 @@ export const Plugin = ({
116123 }
117124
118125 // if iframe has not sent initial request, set up a listener
119- if ( ! communicationReceived && ! inErrorState ) {
120- const listener = postRobot . on (
121- 'getPropsFromParent' ,
122- // listen for messages coming only from the iframe rendered by this component
123- { window : iframeRef . current . contentWindow } ,
124- ( ) : any => {
125- setCommunicationReceived ( true )
126- return iframeProps
127- }
128- )
129- return ( ) => listener . cancel ( )
126+ if ( ! communicationReceived ) {
127+ // avoid setting up twice
128+ if ( ! propsFromParentListenerRef . current ) {
129+ propsFromParentListenerRef . current = postRobot . on (
130+ 'getPropsFromParent' ,
131+ // listen for messages coming only from the iframe rendered by this component
132+ { window : iframeRef . current . contentWindow } ,
133+ ( ) : any => {
134+ setCommunicationReceived ( true )
135+ return iframeProps
136+ }
137+ )
138+ }
139+ // return clean-up function
140+ return ( ) => {
141+ propsFromParentListenerRef . current . cancel ( )
142+ propsFromParentListenerRef . current = null
143+ }
130144 }
131145
132146 // if iframe has sent initial request, send new props
133- if (
134- communicationReceived &&
135- iframeRef . current . contentWindow &&
136- ! inErrorState
137- ) {
147+ // (don't do before to avoid sending messages before listeners
148+ // are ready)
149+ if ( iframeRef . current . contentWindow ) {
138150 postRobot
139151 . send (
140152 iframeRef . current . contentWindow ,
@@ -147,10 +159,32 @@ export const Plugin = ({
147159 } )
148160 }
149161 }
150- // prevCommunicationReceived update should not retrigger this hook
151- // eslint-disable-next-line react-hooks/exhaustive-deps
152162 } , [ memoizedPropsToPass , communicationReceived , inErrorState , alertsAdd ] )
153163
164+ useEffect ( ( ) => {
165+ // return the clean-up function
166+ return setUpCommunication ( )
167+ } , [ setUpCommunication ] )
168+
169+ const handleLoad = useCallback (
170+ ( event ) => {
171+ setCommunicationReceived ( false )
172+
173+ if ( propsFromParentListenerRef . current ) {
174+ propsFromParentListenerRef . current . cancel ( )
175+ propsFromParentListenerRef . current = null
176+ }
177+
178+ // Need to set this up again whenever the iframe contentWindow
179+ // changes (e.g. navigations or reloads)
180+ setUpCommunication ( )
181+ if ( onLoad ) {
182+ onLoad ( event )
183+ }
184+ } ,
185+ [ onLoad , setUpCommunication ]
186+ )
187+
154188 if ( data && ! pluginEntryPoint ) {
155189 return (
156190 < PluginError
@@ -176,7 +210,7 @@ export const Plugin = ({
176210 height : '100%' ,
177211 border : 'none' ,
178212 } }
179- onLoad = { onLoad }
213+ onLoad = { handleLoad }
180214 > </ iframe >
181215 </ div >
182216 )
0 commit comments