Skip to content

Commit 34c4d85

Browse files
committed
change react-retool to functional component
1 parent 46620a5 commit 34c4d85

File tree

2 files changed

+111
-116
lines changed

2 files changed

+111
-116
lines changed

src/App.js

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,51 @@
1-
import Retool from "./components/Retool"
1+
import Retool from "./components/Retool";
2+
import React, { useState, useEffect } from "react";
23

3-
function App() {
4+
const App = () => {
5+
const sample = {
6+
example: "value",
7+
example2: 19999,
8+
example3: { hello: "world" },
9+
input: "",
10+
};
11+
12+
useEffect(() => {
13+
const handler = (event) => {
14+
if (event.data?.type !== "PARENT_WINDOW_QUERY") {
15+
setRetoolData(event.data);
16+
}
17+
};
18+
19+
window.addEventListener("message", handler);
20+
21+
return () => window.removeEventListener("message", handler);
22+
}, []);
23+
24+
const [retoolData, setRetoolData] = useState("");
25+
const [data, setData] = useState(sample);
426
return (
5-
<Retool
6-
url="https://retoolin.tryretool.com/embedded/public/f7607e1f-670a-4ebf-9a09-be54cf17181e"
7-
data={{
8-
example: "value",
9-
}}
10-
></Retool>
11-
)
12-
}
13-
14-
export default App
27+
<div style={{ height: "500px" }}>
28+
<button
29+
onClick={() => {
30+
setData({ ...data, example2: "new value" });
31+
}}
32+
>
33+
This is button
34+
</button>
35+
<input
36+
type="text"
37+
value={data.input}
38+
onChange={(e) => setData({ ...data, input: e.target.value })}
39+
/>
40+
<p id="hello"> Hey123 </p>
41+
<h1> {retoolData} </h1>
42+
<p id="hello"> {JSON.stringify(data)} </p>
43+
<Retool
44+
url="https://support.retool.com/apps/Ben/parent%20window%20query123"
45+
data={data}
46+
></Retool>
47+
</div>
48+
);
49+
};
50+
51+
export default App;

src/components/Retool.js

Lines changed: 62 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,87 @@
1-
import React from "react"
1+
import React, { useEffect, useRef, useState } from "react";
22

3-
class Retool extends React.Component {
4-
constructor(props) {
5-
super(props)
3+
const Retool = ({ data, url }) => {
4+
const embeddedIframe = useRef(null);
5+
const [elementWatchers, setElementWatchers] = useState({});
66

7-
if (!this.props.url)
8-
throw new Error("Please pass a url into the Retool component.")
9-
10-
this.state = {
11-
url: props.url,
12-
elementWatchers: {},
13-
parentData: this.props.data || {},
14-
}
15-
}
16-
17-
componentDidMount() {
18-
this.startListening()
19-
this.startWatchers()
20-
}
21-
22-
startListening = () => {
23-
if (this.iframe) {
24-
window.addEventListener("message", (e) => this.handle(e))
7+
useEffect(() => {
8+
for (const key in elementWatchers) {
9+
const watcher = elementWatchers[key];
10+
watcher.iframe?.contentWindow.postMessage(
11+
{
12+
type: "PARENT_WINDOW_RESULT",
13+
result: data[watcher.selector],
14+
id: watcher.queryId,
15+
pageName: watcher.pageName,
16+
},
17+
"*"
18+
);
2519
}
26-
}
27-
28-
startWatchers = () => {
29-
var watcherKeys = Object.keys(this.state.elementWatchers)
30-
31-
for (var i = 0; i < watcherKeys.length; i++) {
32-
var key = watcherKeys[i]
33-
var watcher = this.state.elementWatchers[key]
34-
var selector = watcher.selector
35-
const value = this.dataFromSelector(selector)
36-
if (value !== watcher.prevValue) {
37-
watcher.prevValue = value
38-
watcher.iframe.contentWindow.postMessage(
39-
{
40-
type: "PARENT_WINDOW_RESULT",
41-
result: value,
42-
id: watcher.queryId,
43-
pageName: watcher.pageName,
44-
},
45-
"*"
46-
)
20+
}, [data, elementWatchers]);
21+
22+
useEffect(() => {
23+
const handler = (event) => {
24+
if (!embeddedIframe?.current?.contentWindow) return;
25+
if (event.data.type === "PARENT_WINDOW_QUERY") {
26+
createOrReplaceWatcher(
27+
event.data.selector,
28+
event.data.pageName,
29+
event.data.id
30+
);
31+
postMessageForSelector("PARENT_WINDOW_RESULT", event.data);
4732
}
48-
}
33+
};
4934

50-
setTimeout(this.startWatchers, 100)
51-
}
35+
window.addEventListener("message", handler);
5236

53-
createOrReplaceWatcher = (selector, pageName, queryId) => {
54-
var watcherId = pageName + "-" + queryId
55-
var watchers = { ...this.state.elementWatchers }
37+
// clean up
38+
return () => window.removeEventListener("message", handler);
39+
}, []);
5640

57-
watchers[watcherId] = {
58-
iframe: this.iframe,
41+
const createOrReplaceWatcher = (selector, pageName, queryId) => {
42+
const watcherId = pageName + "-" + queryId;
43+
const updatedState = elementWatchers;
44+
45+
updatedState[watcherId] = {
46+
iframe: embeddedIframe.current,
5947
selector: selector,
6048
pageName: pageName,
6149
queryId: queryId,
62-
prevValue: null,
63-
}
64-
65-
this.setState({ elementWatchers: watchers })
66-
}
50+
};
6751

68-
dataFromSelector = (selector) => {
69-
// Two places the app might be asking for data:
70-
// 1. The textContent of an HTML element.
71-
// 2. From data passed into this component
72-
const matchingInjectedData = this.state.parentData[selector]
73-
const nodeData = document.querySelector(selector)?.textContent
74-
return matchingInjectedData || nodeData || null
75-
}
52+
setElementWatchers(updatedState);
53+
};
7654

77-
postMessageForSelector = (messageType, eventData) => {
78-
const maybeData = this.dataFromSelector(eventData.selector)
55+
const postMessageForSelector = (messageType, eventData) => {
56+
const maybeData = data[eventData.selector];
7957

8058
if (maybeData) {
81-
this.iframe.contentWindow.postMessage(
59+
embeddedIframe.current.contentWindow.postMessage(
8260
{
8361
type: messageType,
8462
result: maybeData,
8563
id: eventData.id,
8664
pageName: eventData.pageName,
8765
},
8866
"*"
89-
)
67+
);
9068
} else {
9169
console.log(
9270
`Not sending data back to Retool, nothing found for selector: ${eventData.selector}`
93-
)
94-
}
95-
}
96-
97-
handle = (event) => {
98-
if (!this.iframe?.contentWindow) return
99-
if (event.data.type === "PARENT_WINDOW_QUERY") {
100-
this.createOrReplaceWatcher(
101-
event.data.selector,
102-
event.data.pageName,
103-
event.data.id
104-
)
105-
this.postMessageForSelector("PARENT_WINDOW_RESULT", event.data)
71+
);
10672
}
107-
108-
if (event.data.type === "PARENT_WINDOW_PREVIEW_QUERY") {
109-
this.postMessageForSelector("PARENT_WINDOW_PREVIEW_RESULT", event.data)
110-
}
111-
}
112-
113-
render() {
114-
return (
115-
<iframe
116-
height="100%"
117-
width="100%"
118-
frameBorder="none"
119-
src={this.state.url}
120-
ref={(e) => {
121-
this.iframe = e
122-
}}
123-
title="retool"
124-
></iframe>
125-
)
126-
}
127-
}
128-
129-
export default Retool
73+
};
74+
75+
return (
76+
<iframe
77+
height="100%"
78+
width="100%"
79+
frameBorder="none"
80+
src={url}
81+
ref={embeddedIframe}
82+
title="retool"
83+
></iframe>
84+
);
85+
};
86+
87+
export default Retool;

0 commit comments

Comments
 (0)