As a simple example, suppose I had these two files:
example.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8" />
<title>Button example</title>
<script type="text/javascript" src="ButtonHandler.js"></script>
</head>
<body id="body" onload="init()">
<button onclick=buttonHandler.writeToConsole()>Button</button>
<script>
function init() {
buttonHandler = new ButtonHandler();
}
</script>
</body>
</html>
ButtonHandler.js
function ButtonHandler() {
};
ButtonHandler.prototype.writeToConsole = function () {
console.log('Writing');
}
This simply prints to the console whenever the button is clicked.
Ignore that the ButtonHandler's constructor is empty, and that I could just easily call 'console.log' in the onclick directly. This is a simplified version of an issue I'm having, with several classes.
My question is, how would I go about translating this to React/JSX, ideally without modifying the Javascript files (in this case, just ButtonHandler.js). Ideally this means no exporting/importing, I'd like to do it how the HTML file does it - it just links to the script in the <\head>.
The closest I have is something like this:
convert.jsx
import * as React from 'react';
export default class Entry extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
buttonHandler = new ButtonHandler();
}
render() {
return (
<div>
<title>Button example</title>
<button onclick="buttonHandler.writeToConsole()">Button</button>
</div>
)
}
}
But I get the error that ButtonHandler is undefined. I followed this stackexchange answer and placed
<script type="text/javascript" src="[rest of path...]/ButtonHandler.js"></script>
in the public/index head, and I added the 'window.ButtonHandler' in componentDidMount(), but I still get the error that it's undefined.
Am I doing something wrong, and if not, what other approach can I take?
edit: When I put ButtonHandler.js in the public folder with index, and I console log the window, I see it appear as a function of window, like the stackexchange answer describes. This doesn't happen when I have it in another folder, though. Same error however.
edit 2: Seems the only solution is to put ButtonHandler.js in the public folder and then call it in the constructor like the selected answer says. Then add a
<button onClick={() => this.buttonHandler.writeToConsole()}>Button</button>
to call it.
In create react app, you should be able to add any js files to your public folder for use in your project. You just need to reference the files in your script like:
<script type="text/javascript" src="%PUBLIC_URL%/ButtonHandler.js"></script>
That will make sure that it looks in the public folder when building.
The only problem with that is that the files won't be minified in the bundle.
Edit
You will have to reference the global variable inside your component as well.
/* global ButtonHandler */
import * as React from 'react';
export default class Entry extends React.Component {
constructor(props) {
super(props);
this.buttonHandler = new ButtonHandler();
}
render() {
return (
<div>
<title>Button example</title>
<button onclick={this.buttonHandler.writeToConsole}>Button</button>
</div>
)
}
}
You'll want to import that ButtonHandler js code. If it's not something you've written yourself, the easiest thing to do would be to see if it already exists as a React package. If it's your own file, then you'll want to export the functions in ButtonHandler.js, import ButtonHandler in your React component, then you'll have access to them in the component.
ButtonHandler.js
export function writeToConsole() {
console.log('Writing');
}
convert.jsx
import React, { Component } from 'react';
import { writeToConsole } from './ButtonHandler';
export default class Entry extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<title>Button example</title>
<button onclick={this.writeToConsole}>Button</button>
</div>
)
}
}
Related
I am struggling to collect custom Simple Analytics metadata in my Next.js app. Looking at their docs, I can either set metadata on the window object (link) or add it via a callback function (link).
My Next.js app looks as follows:
_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document'
export default class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
[...snip...]
<script dangerouslySetInnerHTML={{
__html: `window.sa_event=window.sa_event||function(){a=[].slice.call(arguments);sa_event.q?sa_event.q.push(a):sa_event.q=[a]};`
}}/>
</Head>
<body>
<Main />
<NextScript />
// NOTE 3: Where can I define `func` so I have access to the router/query string params?
<script async defer data-metadata-collector="func" src="https://scripts.simpleanalyticscdn.com/latest.js"></script>
<noscript><img src="https://queue.simpleanalyticscdn.com/noscript.gif" alt=""/></noscript>
</body>
</Html>
)
}
}
page.js
import Head from 'next/head'
import { useRouter } from 'next/router'
import Layout from '../components/layout'
export default function Page() {
const router = useRouter()
const i = router.query.i
return (
<>
<Head>
[...snip...]
</Head>
// NOTE 1: This does not work
<script>
sa_metadata = { i: i }
</script>
// NOTE 2: I cannot access `i` here
<script dangerouslySetInnerHTML={{
__html: `window.sa_metadata={ i: i };`
}}/>
[...snip...]
</>
)
}
As you can see, I tried two ways of setting metadata on window (NOTES 1 and 2) and I got stuck on the callback function (NOTE 3). Would appreciate any help in moving this forward.
Created a react app and then converted it into a single spa react app using
https://www.youtube.com/watch?v=W8oaySHuj3Y
When a hit is made to http://localhost:8080/org-app.js I get a response of the javascript files.
Also when http://single-spa-playground.org/playground/instant-test?name=#org/app&url=8080 the app loads.
However now trying to import the same app in an html page does not replaces the tag.However, it loads the component know this because of the api calls being made and redux store being loaded.
Have not done singleSpa.registerApplication even if I do it is it necessary a root component needs to be made to register the application.
org-app.js
import React from "react";
import ReactDOM from "react-dom";
import singleSpaReact from "single-spa-react";
import Root from "./root.component";
import il8n from "./i18n";
const domElementGetter = () => {
let el = document.getElementById("example-app");
if (!el) {
el = document.createElement("div");
el.id = "example-app";
document.body.appendChild(el);
}
return el;
};
const lifecycles = singleSpaReact({
React,
ReactDOM,
il8n,
rootComponent: Root,
errorBoundary(err, info, props) {
// Customize the root error boundary for your microfrontend here.
return null;
},
domElementGetter,
});
export const { bootstrap, mount, unmount } = lifecycles;
TestPage.html Directly opened
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<script src="https://cdn.jsdelivr.net/npm/regenerator-runtime#0.13.5/runtime.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/import-map-overrides#2.3.0/dist/import-map-overrides.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs#6.8.3/dist/system.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs#6.8.3/dist/extras/amd.js"></script>
<meta name="importmap-type" content="systemjs-importmap" />
<script type="systemjs-importmap">
{
"imports": {
"single-spa": "https://cdn.jsdelivr.net/npm/single-spa#5.9.0/lib/system/single-spa.min.js",
"react": "https://cdn.jsdelivr.net/npm/react#16.13.1/umd/react.production.min.js",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom#16.13.1/umd/react-dom.production.min.js",
"rxjs": "https://cdn.jsdelivr.net/npm/#esm-bundle/rxjs/system/es2015/rxjs.min.js",
"rxjs/operators": "https://cdn.jsdelivr.net/npm/#esm-bundle/rxjs/system/es2015/rxjs-operators.min.js"
}
}
</script>
<script type="systemjs-importmap">
{
"imports": {
"#example/app": "http://localhost:8080/org-app.js"
}
}
</script>
</head>
<body>
<script>
System.import("#example/app");
</script>
<div id="example-app"></div>
<h1></h1>
<p>My first paragraph.</p>
<import-map-overrides-full
show-when-local-storage="devtools"
dev-libs
></import-map-overrides-full>
</body>
</html>
The problem here is that you're trying to bypass single-spa entirely. The root config should be where the applications get registered which creates the association between routes and applications and dictates when they will be mounted/unmounted. Simply calling System.import("#example/app"); is not enough because the applications do not manage their own lifecycles. Instead you could do something like this:
System.import("single-spa").then(({ registerApplication, start }) => {
registerApplication({
name: "#example/app",
app: () => System.import("#example/app"),
activeWhen: ["/"],
});
start({
urlRerouteOnly: true,
});
});
I cannot see a benefit do doing it this way over what create-single-spa provides.
Lastly, it seems that you're trying to do this to control where the applications are being mounted to. There are two ways to do this with what single-spa already provides:
Using single-spa-layout you can simply create all the intermediate DOM nodes
Use the domElementGetter option of the corresponding framework helpers to designate where the application should mount to.
I'm new to React. I have a local development server (node http-server) and have installed material-ui package.
According to the material-ui docs, to add a component, I simply import it like any other module:
import Button from '#material-ui/core/Button';
However, when I try this, my webserver is returning a 404 and Firefox console outputs this: Loading module from “http://localhost:8080/node_modules/#material-ui/core/Button/” was blocked because of a disallowed MIME type (“text/html”)
The directory is accessible because I can reference the ./node_modules/#material-ui/core/Button/Button.js file and it is returned by http-server. But if I import that file, I get
SyntaxError: import not found: default.
I've tried importing index.js from that directory, but get same error.
How do I import the Button component (as shown in the material-ui example) correctly?
Here is my code (being served by http-server on localhost:8080)
(I am using npx babel to compile the JSX to js before serving)
test.html:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Test React</title>
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
</head>
<body>
<div id="app"></div>
<!-- React scripts -->
<script type="module" src="./test.js"></script>
</body>
</html>
test.js:
import { Mod1 } from './module1.js';
export class TestComponent extends React.Component
{
render()
{
return <h1>Hello world!</h1>;
}
}
export const TestC2 = () =>
{
return <h2>This is a h2</h2>;
}
ReactDOM.render(<div><TestComponent /><TestC2 /><Mod1 /></div>, document.getElementById('app'));
module1.js
// module1
import Button from './node_modules/#material-ui/core/Button';
export class Mod1 extends React.Component
{
constructor(props)
{
super(props);
this.state = {};
}
componentWillMount(props)
{
console.log("willMount", props);
}
componentDidMount(pp, ps)
{
console.log("didMount", pp, ps);
}
render()
{
console.log('render', this.props, this.state);
return <Button />;
}
}
Based on official how to:
Add React to Website at https://reactjs.org/docs/add-react-to-a-website.html
I created test.html with content:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Add React in One Minute</title>
</head>
<body>
<h2>Add React in One Minute</h2>
<p>This page demonstrates using React with no build tooling.</p>
<p>React is loaded as a script tag.</p>
<!-- We will put our React component inside this div. -->
<div id="like_button_container"></div>
<!-- Load React. -->
<!-- Note: when deploying, replace "development.js" with "production.min.js". -->
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<!-- Load our React component. -->
<script src="test.js"></script>
</body>
</html>
And test.js:
'use strict';
const e = React.createElement;
class LikeButton extends React.Component {
constructor(props) {
super(props);
this.state = { liked: false };
}
componentDidMount() {
axios.get(`http://localhost:4000/api/v1/cars`)
.then(result => {
console.log(result.data[0].make);
})
}
render() {
if (this.state.liked) {
return 'You liked this.';
}
return e(
'button',
{ onClick: () => this.setState({ liked: true }) },
'Like'
);
}
}
const domContainer = document.querySelector('#like_button_container');
ReactDOM.render(e(LikeButton), domContainer);
The code above is working well.
I'm able to press Like button and see the change, also able to use libraries such as Axios.
Now I want to open http://localhost/test.html?param1=111¶m2=222 and get these param1 and param2 variables inside test.js - React. Is that possible? How to achieve this?
Many thanks
Just as you perform a fetch in ComponentDidMount, you can check query params in the same lifecycle event. Building on the link shared by #Olian04, here's how that'd look:
componentDidMount() {
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has("param1")) {
console.log(urlParams.get("param1"));
} else {
console.log("param1 was not found");
}
if (urlParams.has("param2")) {
console.log(urlParams.get("param2"));
} else {
console.log("param2 was not found");
}
}
im trying extends the paper-input component from Polymer 3 like documentation says but i cant.
https://polymer-library.polymer-project.org/3.0/docs/devguide/dom-template#inherit
In the next example you can try using the snippet. The problem is the import of paper-input hasnt PaperInputElement export so i cant import. You must to see here : https://unpkg.com/#polymer/polymer/polymer-element.js?module
Could you help me?
<script type='module'>
import 'https://unpkg.com/#polymer/paper-input#3.0.1/paper-input.js?module';
import {PolymerElement, html} from 'https://unpkg.com/#polymer/polymer/polymer-element.js?module';
class DmInput extends PaperInputElement {
static get template() {
return html`
<style>
</style>
<p>hi</p>
<p>${super.template}</p>
`;
}
}
window.customElements.define('dm-input', DmInput);
</script>
<dm-input></dm-input>
I believe you're attempting to import the wrong file here, Paper-input is just a ui component that uses the PaperInputBehavior. Try replacing PaperInputElement in this case with the behavior as all functions, properties and events that the input element uses come from it.
class DmInput extends PaperInputBehavior {
static get template() {
return html`
<style>
</style>
<p>hi</p>
<p>${super.template}</p>
`;
}
}