How to create and render a React Component after babelify/transpiling? - javascript

I have a hello world react component that is written in JSX, transpiled with babel, and then included in the hello.html template of a Flask app. What I have working is creating and rendering the component before transpiling as such:
const hello = <Hello name="world" />;
ReactDOM.render(hello, document.getElementById('hello'));
How can I do those two steps in a <script> tag in my hello.html template? My goal is to be able to pass that name variable from the template to the component and then render it.
A little more context:
The JSX hello.js looks like this:
import React from 'react';
import ReactDOM from 'react-dom'
import { render } from 'react-dom'
class Hello extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<div>Hello {this.props.name}!!!</div>
)
}
}
//The following works:
//const hello = <Hello name="world" />;
//ReactDOM.render(hello, document.getElementById('hello'));
hello.html looks like this:
<html>
<head>
</head>
<body>
<div>ASDF</div>
<div id="hello"></div>
</body>
{# The following line is a post babelify (transpiled) hello.js #}
<script type="text/javascript" src="{{ url_for('static', filename='js/hello.js') }}"></script>
<script type="text/javascript">
{#
What goes here? The code in the above section does not work.
The transpiled code defines a "var Hello = /*#__PURE__*/ function (_React$Component) { ...".
const hello = Hello(); does not throw an error, but also does not render or pass an argument.
hello.render(); is also something that I have tried, along with arguments for div/id to render in and name.
#}
</script>
</html>
Correction: Calling Hello() does not throw an error if the script is text/babel, in which case the script probably isn't doing anything.
The Flask route looks like this:
#app.route(u'/')
def index():
return render_template(u'hello.html', name="universe")

Two ways you can pass variables from your server application to react component:
Use the html data-variable prop.
Create a global variable. Something like window.variable
Then you should be able to access variable as a props like props.variable in your react-component.
My recommended approach I would take is to use a bundler such as SystemJS (version 2), and you will have something like the following:
<!DOCTYPE html>
<html>
<head>
<script src="node_modules/core-js-bundle/minified.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script type="systemjs-importmap" src="systemjs.imports.json"></script>
<script src="node_modules/systemjs/dist/system.min.js"></script>
<script src="node_modules/systemjs/dist/extras/named-exports.min.js"></script>
<script>
System.import('../.playground/index.js').catch(function (err) { console.error(err); });
</script>
</head>
<body>
<div>ASDF</div>
<div id="hello"></div>
</body>
</html>
And index.js will look something like this
ReactDOM.render(
(< Hello/>),
document.getElementById('app')
);
Then your systemjs-importmap will look like this
{
"imports": {
"react": "../node_modules/react/umd/react.production.min.js",
"react-dom": "../node_modules/react-dom/umd/react-dom.production.min.js",
// ... other named exports you want to add like the Hello component here
}
}

Related

How to call Vue 3 method from outside the app

what I want is to call a method, declared in vue 3 app form outside the component of the page. So what I did sofar:
App.vue
<script setup>
function test(){
console.log('test');
}
</script>
vue.js
import { createApp } from 'vue'
import App from 'App.vue'
window.app = createApp(App).mount('#app')
index.html
<div id="app"></app>
<script src="app.js"></script>
<script>
fuction callTest(){
window.app.test() // <-- this returns undefined
}
</script>
however it worked with vue2. Any idea how to get it work with vue3?
You need to use defineExpose inside in the first file in order to use it outside the component:
<script setup>
function test(){
console.log('test');
}
defineExpose({
test
})
</script>

How to make script type both text/babel and module?

It works perfectly because I haven't included JSX, but when I replace the script type with text/babel, it doesn't work because the module fails to load. browser.js the Babel compiler.
Here... JSX works only when i replace script type with text/babel but the problem is module fails to load since the script is not module. Any idea how make it work with JSX?
<div id="root">
</div>
<script type="module">
import './react.min.js';
import './react-dom.min.js';
import './browser.js';
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.toWhat}`);
}
}
ReactDOM.render(
React.createElement(Hello, {toWhat: 'World'}, null),
document.getElementById('root')
);
</script>
UPDATE July 2021
As per mh sattarian's answer you now don't need data-plugins="transform-es2015-modules-umd" to use native es6 module's import/export etc. Instead you simply add data-type="module"
Original Answer
Just in case if some one comes here looking for answer
There is a support for data-plugins and data-presets in babel standalone
<script data-plugins="transform-es2015-modules-umd" type="text/babel">
see more here
Babel standalone
As mentioned in the docs, it's added in: v7.10.0.
If you want to use your browser's native support for ES Modules, you'd
normally need to set a type="module" attribute on your script tag.
With #babel/standalone, set a data-type="module" attribute instead,
like this:
<script type="text/babel" data-type="module">
I think the question was if it is possible to use a script tag with two or more types at the same time (e.g. something like type="module, txt/babel"). As far as I know the answer is NO.
JonDotsoy's answer helps with reduce typing React.createElement over and over again but even with such a "variable shortcut" it's not as comfortable as JSX when using larger templates with nested elements because h('div', {}, 'hello!!')... is difficult to maintain in such cases.
The only way I found to combine native browser module support and in-browser Babel for JSX without using any build tool is this ... a rather dirty hack that uses eval and should not be used for production apps:
index.html
<body>
<div id="app"></div>
<!-- Scripts ------- -->
<script src="vendor/js/babel.min.js"></script>
<script src="vendor/js/react.development.js"></script>
<script src="vendor/js/react-dom.development.js"></script>
<script src="app/app.js" type="module"></script>
</body>
app/app.js
import ComponentOne from "./ComponentOne.js";
let template = `
<div>
<h1>Heading</h1>
<hr />
<ComponentOne msg="MsgText-ComponentOne" />
</div>
`;
const App = () => {
return (
eval(Babel.transform(template, { presets: ['es2017', 'react'] }).code)
);
};
ReactDOM.render(
React.createElement(App, null),
document.getElementById("app")
);
app/ComponentOne.js
import ComponentTwo from "./ComponentTwo.js";
let template = `
<div>
<h2>This is ComponentOne</h2>
<p key="2">Property "msg" content: {props.msg}</p>
<ComponentTwo msg="MsgText-ComponentTwo" />
</div>
`;
const ComponentOne = (props) => {
return(
eval(Babel.transform(template, { presets: ['es2017', 'react'] }).code)
);
};
export default ComponentOne;
app/ComponentTwo.js
let template = `
<div>
<h2>This is ComponentTwo</h2>
<p key="2">Property "msg" content: {props.msg}</p>
</div>
`;
const ComponentTwo = (props) => {
return(
eval(Babel.transform(template, { presets: ['es2017', 'react'] }).code)
);
};
export default ComponentTwo;
you may prefer to use an alias to createElement. This way is faster for the engine.
For example use h or e.
const { createElement: h } = React;
const App = () => {
return h('div', {}, 'hello!!');
}
Otherwise, it is possible to use #babel/standalone module, please see more on here https://babeljs.io/docs/en/next/babel-standalone.html.
<div id="app"></div>
<!-- Load Babel -->
<script src="https://unpkg.com/#babel/standalone/babel.min.js"></script>
<!-- Your custom script here -->
<script type="text/babel">
const { render } = ReactDOM;
const App = () => {
return <div>hello!!</div>
}
render(<App />, document.getElementById('app'));
</script>

Why do I need import React statement even if I don't use React explicitly?

I have an React App, following is JavaScript code
import React from 'react';
import ReactDOM from 'react-dom';
const App = function(){
return <div>Hi</div>
}
ReactDOM.render(<App />, document.querySelector('.container'));
And the HTML file is as following.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/style/style.css">
<link rel="stylesheet" href="https://cdn.rawgit.com/twbs/bootstrap/48938155eb24b4ccdde09426066869504c6dab3c/dist/css/bootstrap.min.css">
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAq06l5RUVfib62IYRQacLc-KAy0XIWAVs"></script>
</head>
<body>
<div class="container"></div>
</body>
<script src="/bundle.js"></script>
</html>
The question I don't understand is that if I remove import React from 'react', it will show error message like below.
Uncaught ReferenceError: React is not defined
But I don't use React in my code explicitly anywhere, why would it show a message like this. Can anyone tell me what's going on under the hood?
UPDATE:
Not exactly the same question with this one, since what I have in my code is just an individual component, not involving any parent component.
Using JSX (<App />) is just a syntatic sugar for React.createElement().
So when your code is transpiled to pure javascript, references to React will appear there, so you need the import for that.
So yes, you're using it, although you don't see it
See what is your code transpiled to here
'use strict';
var _reactDom = require('react-dom');
var _reactDom2 = _interopRequireDefault(_reactDom);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var App = function App() {
return React.createElement(
'div',
null,
'Hi'
);
};
_reactDom2.default.render(React.createElement(App, null), document.querySelector('.container'));

JavaScript ReferenceError: $ is not defined

I have an html file that looks like this:
<!DOCTYPE html>
<html>
<head>
<title>AppName</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
</head>
<body>
<div id="container"></div>
<script src="/bundle.js"></script>
</body>
</html>
And I have a javascript file that contains this:
EDIT: included jQuery via npm, and it seems to have worked.
export function getData(arg, callback) {
var $ = require('jQuery'); // This line was added via EDIT above.
$.ajax({
type: "GET",
url: "/get_data",
data: {data: arg},
success: callback
});
}
When I go to my page and execute the function, I eventually get this error:
Uncaught ReferenceError: $ is not defined
at Object.getData (http://localhost:1234/static/bundle.js:21665:6)
Based on the questions/answers I've seen on SOF, it seems like the solution was to include the jquery script in my html file.
Any ideas what I might be doing wrong?
EDIT: As per request, here is my react index.js file. The error is coming from within the searchApi.js file:
import React from 'react';
import ReactDOM from 'react-dom';
import SearchBar from './components/SearchBar';
import * as searchApi from './api/searchApi';
class App extends React.Component {
constructor() {
super();
this.getData = this.getData.bind(this);
this.processData = this.processData.bind(this);
}
getData(data) {
searchApi.getData(data, this.processData);
}
processData(payload) {
console.log(payload);
}
render() {
return (
<div>
<SearchBar getData={this.getData}/>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);

Using globally defined script inside the component

I am using global script declaration inside index.html
<!DOCTYPE html>
<html>
<head>
<script src='https://js.espago.com/espago-1.1.js' type='text/javascript'></script>
...
</head>
<body>
...
</body>
Now I want to use it inside the component.
import * as React from "react";
import * as $ from "jquery";
//how to import Espago?
export default class EspagoPayment extends React.Component<any, any> {
componentDidMount() {
$("#espago_form").submit(function(event){
var espago = new Espago({public_key: 'xxx', custom: true, live: false, api_version: '3'});
espago.create_token({
...
});
});
}
render() {
return (
...
);
}
}
Webpack gives an error on build.
error TS2304: Cannot find name 'Espago'
How to get Espago visible inside the component?
Maybe there is other way to link to online js resource?
You have to tell TypeScript that it's defined somewhere else.
declare var Espago: any;
See https://stackoverflow.com/a/13252853/227299

Categories