Skip to content

Commit 1f9d832

Browse files
authored
Revert "Convert Retool.js to use React hooks"
1 parent 6f6c327 commit 1f9d832

File tree

3 files changed

+121
-125
lines changed

3 files changed

+121
-125
lines changed

src/App.js

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

4-
const App = () => {
5-
const sample = {
6-
example1: "",
7-
example2: false,
8-
input: "",
9-
};
10-
11-
useEffect(() => {
12-
const handler = (event) => {
13-
if (event.data?.type !== "PARENT_WINDOW_QUERY") {
14-
setRetoolData(event.data);
15-
}
16-
};
17-
18-
window.addEventListener("message", handler);
19-
20-
return () => window.removeEventListener("message", handler);
21-
}, []);
22-
23-
const [retoolData, setRetoolData] = useState("");
24-
const [data, setData] = useState(sample);
3+
function App() {
254
return (
26-
<div>
27-
<h1> React-Retool</h1>
28-
<button
29-
onClick={() => {
30-
setData({ ...data, example2: !data.example2 });
31-
}}
32-
>
33-
Click me!
34-
</button>
35-
<br />
36-
<br />
37-
<label> Share something: </label>
38-
<input
39-
type="text"
40-
value={data.input}
41-
onChange={(e) => setData({ ...data, input: e.target.value })}
42-
/>
43-
<br />
44-
<br />
45-
<Retool
46-
url="https://support.retool.com/embedded/public/cb9e15f0-5d7c-43a7-a746-cdec870dde9a"
47-
data={data}
48-
height="700px"
49-
width="1000px"
50-
></Retool>
51-
<h1> {retoolData} </h1>
52-
</div>
53-
);
54-
};
55-
56-
export default App;
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

src/components/Retool.js

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

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

7-
/* Retool passes up the list of elements to watch on page load */
8-
useEffect(() => {
9-
for (const key in elementWatchers) {
10-
const watcher = elementWatchers[key];
11-
watcher.iframe?.contentWindow.postMessage(
12-
{
13-
type: "PARENT_WINDOW_RESULT",
14-
result: data[watcher.selector],
15-
id: watcher.queryId,
16-
pageName: watcher.pageName,
17-
},
18-
"*"
19-
);
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 || {},
2014
}
21-
}, [data, elementWatchers]);
22-
23-
/* On page load, add event listener to listen for events from Retool */
24-
useEffect(() => {
25-
/* Handle events - if PWQ then create/replace watchers -> return result */
26-
const handler = (event) => {
27-
if (!embeddedIframe?.current?.contentWindow) return;
28-
if (event.data.type === "PARENT_WINDOW_QUERY") {
29-
createOrReplaceWatcher(
30-
event.data.selector,
31-
event.data.pageName,
32-
event.data.id
33-
);
34-
postMessageForSelector("PARENT_WINDOW_RESULT", event.data);
35-
}
36-
};
15+
}
3716

38-
window.addEventListener("message", handler);
17+
componentDidMount() {
18+
this.startListening()
19+
this.startWatchers()
20+
}
3921

40-
return () => window.removeEventListener("message", handler);
41-
}, []);
22+
startListening = () => {
23+
if (this.iframe) {
24+
window.addEventListener("message", (e) => this.handle(e))
25+
}
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+
)
47+
}
48+
}
4249

43-
/* Creates or updates the list of values for us to watch for changes */
44-
const createOrReplaceWatcher = (selector, pageName, queryId) => {
45-
const watcherId = pageName + "-" + queryId;
46-
const updatedState = elementWatchers;
50+
setTimeout(this.startWatchers, 100)
51+
}
4752

48-
updatedState[watcherId] = {
49-
iframe: embeddedIframe.current,
53+
createOrReplaceWatcher = (selector, pageName, queryId) => {
54+
var watcherId = pageName + "-" + queryId
55+
var watchers = { ...this.state.elementWatchers }
56+
57+
watchers[watcherId] = {
58+
iframe: this.iframe,
5059
selector: selector,
5160
pageName: pageName,
5261
queryId: queryId,
53-
};
62+
prevValue: null,
63+
}
64+
65+
this.setState({ elementWatchers: watchers })
66+
}
5467

55-
setElementWatchers(updatedState);
56-
};
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+
}
5776

58-
/* Checks for selectors for data and posts message for Retool to read */
59-
const postMessageForSelector = (messageType, eventData) => {
60-
const maybeData = data[eventData.selector];
77+
postMessageForSelector = (messageType, eventData) => {
78+
const maybeData = this.dataFromSelector(eventData.selector)
6179

6280
if (maybeData) {
63-
embeddedIframe.current.contentWindow.postMessage(
81+
this.iframe.contentWindow.postMessage(
6482
{
6583
type: messageType,
6684
result: maybeData,
6785
id: eventData.id,
6886
pageName: eventData.pageName,
6987
},
7088
"*"
71-
);
89+
)
7290
} else {
7391
console.log(
7492
`Not sending data back to Retool, nothing found for selector: ${eventData.selector}`
75-
);
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)
76106
}
77-
};
78-
79-
return (
80-
<iframe
81-
height={height ?? "100%"}
82-
width={width ?? "100%"}
83-
frameBorder="none"
84-
src={url}
85-
ref={embeddedIframe}
86-
title="retool"
87-
></iframe>
88-
);
89-
};
90-
91-
export default Retool;
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

src/index.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import React from "react";
2-
import ReactDOM from "react-dom";
3-
import "./index.css";
4-
import App from "./App";
1+
import React from "react"
2+
import ReactDOM from "react-dom"
3+
import "./index.css"
4+
import App from "./App"
55

66
ReactDOM.render(
77
<React.StrictMode>
88
<App />
99
</React.StrictMode>,
1010
document.getElementById("root")
11-
);
11+
)

0 commit comments

Comments
 (0)