I am new to React and am looking for the React equivalent of this JQuery approach to including analytics throughout my application.
Typically I would:
Include the 3rd party library on the html pages. Easy enough to put on the index.html page but I don't know if that is best practice.
<script src="http://path/to/script/utag.js" />
Then I can interact with the library as long as it has loaded, which I can verify using JQuery window.load. This script will run fine on a plain html page, but I am trying to find the equivalent best practice way of doing this in my react app. I don't want to introduce jquery and currently my React container will tell me that utag is not defined if I try referencing utag in a function.
<script>
$(window).load(function() {
utag.link({ "event_name" : "locale_select", "language" :
utag_data.language, "currency" : utag_data.currency } );
});
</script>
I'm new to React so any help would be great. I know that my project is not using webpack, it's using react-scripts and was started using the create-react-app utility.
According to this issue on GitHub if you are using create-react-app, if you want to use global variables that imported or created in your index.html file in your react script, you must use window.variable_name.
In your case, this will probably work
import React from "react"
class App extends React.Component {
componentDidMount() {
window.utag.link({ "event_name" : "locale_select", "language" :
window.utag_data.language, "currency" : window.utag_data.currency } );
}
render() {
return <div />;
}
}
import someLibrary from 'some-library';
class App extends React.Component {
componentDidMount() {
someLibrary();
}
render() {
return <div />;
}
}
Its important to understand the statement inside the Component offered above. componentDidMount() is a lifecycle method that gets called automatically after the Component has been rendered to the screen. Then inside of it you call your someLibrary(). Depending on what type of third-party library you are talking about will dictate what you may need to also pass into someLibrary().
This is how Reactjs interacts with third-party libraries, because typically third-party libraries do not know how to be in a React ecosystem. They don't have any idea what a render() method is or what JSX is. So this is the general way of making third-party libraries work nicely with React.
If you are using create-react-app, then webpack is being used to bundle your javascript. Here's the documentation on installing dependencies with create-react-app.
To include you library, you should install it as an npm package, and import it into the file where you want to use it. Webpack will include it in the bundle and everything should just work.
So, Install the library with npm install some-library. Import it into a file and call it from a component:
import someLibrary from 'some-library';
class App extends React.Component {
componentDidMount() {
someLibrary();
}
render() {
return <div />;
}
}
Related
im actually not sure if this is possible , but my idea is im writing a library which will be used in another app. In my library, it depends on an external dependencies, lets call it external-A.
Example in my code
import {functionA} from 'external-A'
export function helper() {
return functionA
}
The problem here is for some reason, i dont want to put external-A as dependencies in my library package.json ( i put it as peerDependency now), so it may not install together when user install my library, and may throw error here.
Is there anyway to write the code that when the app load this helper function, it will try to import or analyze whether the app has installed external-A or not. If no, it will return another fallback function that i define
Example of idea ( but i think its not working yet)
import {functionA} from 'external-A'
function fallBackHelper() { console.log('fallbackHelper') }
export function helper() {
return functionA ?? fallbackHelper
}
Abit of background:
I'm using React, and my library use simple tsc to build the code. And the app assume using webpack.
I am trying to use vuejs-datepicker in a nuxt app, everything is done by nuxt plugin usage standarts.
plugins/vue-datepicker.js
import Vue from 'vue'
import Datepicker from 'vuejs-datepicker'
Vue.component('Datepicker', Datepicker)
nuxt.config.js
plugins: [
{ src: '~/plugins/vue-datepicker', ssr: false }
],
But even when it is not used I am getting its dist uploaded in the vendors/app....js after the build. How can make nuxt create a separate chunk for it and import that chunk only in the pages which are using it?
So yeah, there is basically a feature request open for this kind of use-case.
But looking at the Nuxt lifecycle, it looks like the plugins are imported even before the VueJS instance is done. So, you cannot lazy load it if it's done ahead of Vue.
But, you can totally import vuejs-datepicker on the page itself, rather than on the whole project. This may be enough
import Datepicker from 'vuejs-datepicker' // then simply use `Datepicker` in the code below
If it's not, you can maybe try this solution: https://github.com/nuxt/nuxt.js/issues/2727#issuecomment-362213022
// plugins/my-plugin
import Vue from 'vue'
export default () => {
// ...
Vue.use(....)
}
// adminLayouts
import myPlugin from '~/plugins/my-plugin'
export default {
created() {
myPlugin()
}
}
So, the downside is that you have to import the component each time that you need it rather than having it globally but it also allows you to load it only on the concerned pages too and have it chunked per page/component.
If you were trying to find a way to split the component from vendor but you were getting a document is not defined error you can use this syntax to import your component, it will create a separate chunk with your component and use it only in client-side.
components: {
Datepicker: () => import('vue-datepicker')
}
Also, it would be helpful to wrap your component in <client-only> tag for most of the cases.
The plugin I was trying to import used window. For this reason, any other suggested workaround still caused nuxt to crash or error in my case. I searched the whole wide web and the solution below is the only one that allows my app to run.
Instead of importing the plugin with an ES6 import, you can import it in your mounted hook, which should run in the client only. So:
async mounted() {
const Datepicker = await import('vuejs-datepicker');
Vue.use(Datepicker);
}
I do not know about the specific plugin you are trying to use, but in my case I had to call Vue.use() on the default property of the plugin, resulting in Vue.use(MyPlugin.default).
There a UMD bundle with React application that is hosted on CDN and is loaded dynamically when needed. React and ReactDOM are not bundled and it's expected that environment will have them available. The whole application is built using Webpack. I have the following code with SystemJS#0.21 that works:
import React from 'react';
import ReactDOM from 'react-dom';
import SystemJS from 'systemjs/dist/system-production'; // 0.21
SystemJS.registry.set('React', SystemJS.newModule(React));
SystemJS.registry.set('ReactDOM', SystemJS.newModule(ReactDOM));
const URL = 'https://example.com/bundle.js'; // UMD bundle
class App extends React.Component {
componentDidMount() {
SystemJS.import(URL).then(module => this.setState({ module }));
}
render() {
const Component = this.state.module?.default;
return Component ? <Component /> : null;
}
}
Is there a way to make it work with SystemJS#6.0, or load any other way? It works now but using old version sits uneasily with me. Changing format of downloaded package from UMD to SystemJS is next to impossible because there's a lot of consumers that I don't control.
I managed to get SystemJS 6.6.1 working in an Angular project in order to laod a UMD bundle.
You can find the details over here: https://stackoverflow.com/a/63917606/2528609.
The most important part, I think, is that you need to load an additional script to handle UMD modules => "node_modules/systemjs/dist/extras/amd.js"
I'm thinking about building a web application, where people can install plugins. I'd like plugins to be able to define React components that will be rendered to the page, without recompiling the main JavaScript bundle after installing the plugin.
So here's the approach I'm thinking of:
Bundle the main JavaScript with React as an external library, using webpack.
Have plugin authors compile their components with React as an external library as well.
This way, I'm only running one instance of React. I could probably do the same with some other frequently used libraries.
The problem then, is how to dynamically load these plugin components from the server. Let's say I have the following component:
class PluginRenderer extends React.Component{
componentWillMount() {
getPluginComponent(`/plugins/${this.props.plugin}/component.js`).then((com) => {
this.setState({pluginComponent: com});
})
}
render() {
var Plugin = this.state.pluginComponent;
return Plugin ? <Plugin {...this.props} /> : "Loading..."
}
}
How could getPluginComponent be implemented?
It's an interesting problem I also faced some months ago for customer work, and I didn't see too many document approaches out there. What we did is:
Individual plugins will be separate Webpack projects, for which we provide either a template or a CLI tool that generates project templates.
In this project we define Webpack externals for shared vendor libraries already used in the core application: React, Redux, etc. This tells the plugin to not include those in the bundle but to grab them from a variable in window we set in the core app. I know, sounds like sucks, but it's much better than having all plugins re-include 1000s of shared modules.
Reusing this concept of external, the core app also provides some services via window object to plugins. Most important one is a PluginService.register() method which your plugin must call when it's initialized. We're inverting control here: the plugin is responsible to say "hi I'm here, this is my main export (the Component if it's a UI plugin)" to the core application.
The core application has a PluginCache class/module which simply holds a cache for loaded plugins (pluginId -> whatever the plugin exported, fn, class, whatever). If some code needs a plugin to render, it asks this cache for it. This has the benefit of allowing to return a <Loading /> or <Error /> component when a plugin did not load correctly, and so on.
For plugin loading, this PluginService/Manager loads the plugin configuration (which plugins should I load?) and then creates dynamically injected script tags to load each plugin bundle. When the bundle is finished, the register call described in step 3 will be called and your cache in step 4 will have the component.
Instead of trying to load the plugin directly from your component, ask for it from the cache.
This is a very high level overview which is pretty much tied to our requirements back then (it was a dashboard-like application where users could add/remove panels on the fly, and all those widgets were implemented as plugins).
Depending on your case, you could even wrap the plugins with a <Provider store={ theCoreStore }> so they have to access to Redux, or setup an event bus of some kind so that plugins can interact with each other... There is plenty of stuff to figure out ahead. :)
Good luck, hope it helped somehow!
There is a HOC component that you can import to do this. Components are dynamically loaded as micro apps into your host application.
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
window.React = React;
window.ReactDOM = ReactDOM;
ReactDOM.render(<App />, document.getElementById('root'));
// app.js
import React from 'react';
import ReactDOM from 'react-dom';
import MicroApp from '#schalltech/honeycomb-react-microapp';
const App = () => {
return (
<MicroApp
config={{
View: {
Name: 'redbox-demo',
Scope: 'beekeeper',
Version: 'latest'
}
}}
/>
);
});
export default App;
The components are not installed or known at design time. If you get creative, using this approach you could update your components without needing to redeploy your host application.
https://github.com/Schalltech/honeycomb-marketplace#using-micro-apps
I presented an alternative approach on a similar question. Recapping:
On your app
import(/* webpackIgnore: true */'https://any.url/file.js')
.then((plugin) => {
plugin.main({ /* stuff from app plugins need... */ });
});
On your plugin...
const main = (args) => console.log('The plugin was started.');
export { main };
export default main;
See more details on the other question's page.
I use the Angular2-meteor framework but I have a problem when a try to load my javascript file.
Meteor load every file in seem time in a want to my js file loading after the html.
where I have to place the js file for playback after the html code
Which version of Meteor are you using ? If you're using 1.3 , you can put the js files in a special directory named "imports" within your app. Files placed in this directory are not loaded by default and have to be imported using import statement. You can find more details at meteor docs :
Meteor Special Directories
As far as executing code is concerned, you can use the AfterViewInit lifecycle hook of the component. It is triggered once the component's view is fully initialized.
import {AfterViewInit} from '#angular/core'
Then in your Component Class :
export class MyComponent implements AfterViewInit {
ngAfterViewInit() {
// Your code here
}
}