Is it possible to import a react component (.jsx file) from the plugin settings folder and then show it in the main App.js ?
The XD API looks like this:
const fs = require("uxp").storage.localFileSystem;
const folder = await fs.getDataFolder(); // plugin settings folder
const entries = await folder.getEntries("SettingsComponent.js"); // look inside it
but how to insert it in the App.js? Looks like impossible
This doesnāt sound like itās possible. From my understanding of React, it getās compiled to vanilla javascript which will then be executed during runtime. So if you read a file (which happens during runtime), the underlying code isnāt really part of the React framework anymore. You could of course manipulate the HTML DOM manually, but I guess that wasnāt what you intented.
What are you trying to achieve / what is the specific usecase?
1 Like
Hi @simonhenke I just want to import a React component (a file) that is on the setting folder.
this is how you would regularly import a component inside the App.js:
const Component = require("./components/Component");
but, as you can see the plugin settings folder is not that easy to require as a string would be.
And itās definitely necessary for the file to sit inside the settings folder?
Canāt you just have it inside your project structure and import it from there? You could have any other settings file inside the settings folder and read it, then display the component (or not) based on this file or parameter you read.
The problem is that fs.getDataFolder()
is a function, so it retrieves the correct path during runtime. Thatās already to late though, your React imports get processed during the compilation/bundling step.
yes, because I receive that code server side. So I save it in a react file (.js) and if I could import it properly I could use it inside the plugin.
Eventually what I want to do is what .innerHTML would do in vanilla javascript, but thatās not allowed in React (meaning is forbidden). So, I am trying to find a way to actually include some code from outside (in this case the settings folder)
Interesting, Iāve never heard of such a use case. Why does the React Component have to come from a server during runtime?
.innerHTML sets the content of an Element, while respecting HTML syntax, doesnāt it?
So for example you could add '<p>someText</p>
ā to a DIV-Element and it would get added as a child.
A full React Component compiles to more than just HTML-Elements though. There might be instance variables, functions, event callbacks etc.
Iām probably not much of a help, sorry Maybe someone else knows more about such a use case.
1 Like
Maybe it would help to better understand your particular use case.
React generally depends upon bundlers and transpilation, which means itās not going to be able to easily include a file from outside its compilation environment as a component. And then never mind getting everything to be wired up correctly so that events work, state is properly handled, etc.
Iāll note that downloading and executing code off your server is generally not considered a great idea ā there are a whole host of security risks in doing so. While UXP is sandboxed, there are definitely ways beyond accessing the file system that could harm your user should an attacker compromise the code you received from the server.
Iām going to assume a few things at this point, given the name of the component ā Iām guessing this is a list of preferences, and that the list of preferences itself is often modified or generated from elsewhere, and youāre trying to re-use it. In this case, Iād suggest a more generic approach, and encode the settings layout as a JSON file that you could then use to re-construct everything in the UI.
It wouldnāt be horribly difficult to do so with a reasonably simple React component iterating over the fields in the JSON object and creating UI controls based on whatās inside. In fact, thereās probably a few re-usable React components that might already do that for you.
All of that to say that if thatās your use case, you donāt need to download actual code to get the desired functionality.
1 Like
Thanks for the tips, but unfortunately there is more code involved than having a just a few settings written in a json. BTW, itās called āSettingsComponentā because it comes from the settings folder (please read it as āComponentFromSettingsFolderā).
Alright, let me explain what my use-case is:
-
I need to server-side get a new component (saving it as a file, in the plugin settings folder) and then show it in the plugin interface in XD. The component has html-UI, script logic: functions, handle state and more.
-
I want to bypass the waiting for the XD team to publish the plugin on the store, because frequent XDās API changes or my possible mistakes and also because I want to have a few of this functionality received via a payment-gate.
-
The server will be secured via SSL and since UXP is sandboxes, I see a close-to-none risk of someone hacking is any way, shape or form.
-
It would be no problem if React would allow the javascriptās innerHTML method on a ref but, since itās not allowed to use scripts, I cannot take this approach. The only solutions I see is via components-as-a-file to be rendered in the main.The problem is I donāt know how to pick them from that folder.
@kerrishotts Any suggestion is much appreciated
I have tried react-live which is suppose to render react (GitHub - FormidableLabs/react-live: A flexible playground for live editing React components) but it got this error in the plugin UI:
Also tried this approachā¦ nothing
const MyLazyComponent = React.lazy(
() =>
new Promise(async (resolve) => {
const folder = await fs.getDataFolder();
const entries = await folder.getEntries();
// entries.forEach((entry) => console.log(entry.name));
// entries.forEach((entry) => console.log(entry.url));
// entries.forEach((entry) => console.log(entry.nativePath));
const file = await entries.find(
(entry) => entry.name === "ServerCode.js"
);
const module = await import(file.nativePath);
console.log("MODULE", module);
setTimeout(() => resolve(module), 1000);
})
);
return (
<panel className={styles.panel}>
<React.Suspense fallback={<div>Loading...</div>}>
{MyLazyComponent ? <MyLazyComponent /> : null}
</React.Suspense>
</panel>
);
@kerrishotts
EvalError: Code generation from strings disallowed for this context
I wonder why this gets triggered and how to bypass it
I also added in webpack.config.js:
mode: "production",
but no success
Anything that tries to evaluate a string as code (eval
, new Function
, etc.) is not allowed in the XD plugin context. Thereās no real good way around it.
Iāll note that #2 above (avoiding the review process) is not a great reason to try this ā you may end up on the bad end of things if something fails badly enough and enough people report / negatively review the plugin. Iāve included @ashryan in case he has more to say.
For #3, UXP is sandboxed, yes, but that doesnāt mean that an attacker couldnāt use social engineering to try and get your user to do something wrong. SSL is also susceptible to man-in-the-middle attacks (especially on captive networks, such as hotels use).
Of course when downloading things remotely, youāll also have to handle the case where thereās no network connection or the file is corrupt. W/ JS that probably means a broken plugin if thereās a failure, unless you catch that appropriately elsewhere.
You might be able to get SSR/hydration to do something of use here, but generally I think itād be way easier to use a DSL of sorts (w/ JSON or the like) than it would be to try and get a plugin to load a remote React componentā¦
2 Likes