I would like to use jQuery and some other third party libraries not native to React. How can I use them in my React projects? I read componentDidMount is a good place to invoke third party libraries.
Unfortunately, I am unable to use the libraries as I keep getting " is not defined" errors even though I have properly linked script tags to those libraries in my index.html file.
You have two options, both demonstrated by a contrived example where I fade out a unordered list using jQuery. There are pros and cons to both approaches, I highlight both, and then provide my choice.
Option 1: Include the third party library in your index.html file
index.html
<head>
...
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
...
</head>
App.jsx
import React, { Component } from "react";
class App extends Component {
componentDidMount() {
// ** following two lines of code do the same thing
// using the first version, however, could potentially cause errors
// see "Referencing unimported libraries when using create-react-app"
$(this.refs.list).fadeOut(); // version 1
window.$(this.refs.list).fadeOut(); // version 2
}
render() {
return (
<ul ref="list">
<li>Item 1</li>
<li>Item 2</li>
</ul>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
Referencing unimported libraries when using create-react-app
** If you are using create-react-app to scaffold your project, using the first version of code will throw errors. This is because create-react-app's default syntax linter (eslint) will throw errors during project compilation if you try to reference non-imported code (i.e. you never import $ from "jquery" since we reference the library in index.html). So when we referencing jQuery's global $ reference (which is very normal when using jQuery in the browser), we violate the basic principles of building modular JavaScript applications on Node.js. In Node, modules are the way of life and in those modules, we can only reference objects that we explicitly import. Without that explicit mention we technically break convention, hence the complaint from the linter.
Now both you and I know that the $ reference will become available once the application actually runs in the browser. When componentDidMount() is invoked, view has already mounted to the DOM and your third party library (in our example jQuery) is available to reference. Unfortunately the linter will block your react app from rendering because it thinks you are trying to reference undefined variables. Furthermore, the linter is platform agnostic and has no knowledge that your app is running in the browser (since JavaScript is no longer a browser-only language). This may be annoying but the linter is just doing its job, and, to some degree, it's absolutely right.
To avoid this error there are a few options:
Have eslint (the provided linter) ignore the lines where you make
reference the third party libraries using // eslint-disable-next-line (not preferred)
, or
Make use of window (preferred), or
Turn off the linter (not preferred), or
Don't use create-react-app (your preference, not recommend for beginners or even experts for that matter).
The first option can easily become a hassle if you make a lot of calls to the third party library. If you read up on componentDidMount, you know that at this point of invocation, you now have access to the window variable. You can access your library through window if the library attaches itself to the DOM's global window object. In our example, jQuery does just that and we can access jQuery's functionality via window.$
Option 2: Install the library using npm install <package-name> -S and import the library into relevant project files.
Terminal
npm i jquery -S
App.jsx
import React, { Component } from "react";
import $ from "jquery";
class App extends Component {
componentDidMount() {
$(this.refs.list).fadeOut();
}
render() {
return (
<ul ref="list">
<li>Item 1</li>
<li>Item 2</li>
</ul>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
What is the right approach?
There are pros and cons of each approach: with first one you have the possibility of using a library that is hosted on a CDN and bank on the network effects and don't need to import third party code into your codebase. With the latter, you can make your code more readable and avoid the linter from blowing up.
For cons, the first approach may require you to add window to your third party library calls if you're using a linter (and you probably should); in the latter's case, sometimes third party libraries don't have their project code available to install through npm and you must download them manually and manually add them to your project source folder. In that case, using the first approach might make sense so that you don't have to manually import new versions of the library when they're released.
If at the end of all of this, you have failing code:
Ensure you installed the correct package,
Ensure you have included it directly in your index file or
imported it into your project and files where you are making library
specific calls.
If you know of other ways of accomplishing third party library integrations into react or find an error in this post, please feel free to edit this answer or add another answer.
The examples in this answer assume the execution environment is a browser, hence the use of window. If you are building a shell script or using react-native, it would be global, etc.
I needed to use the third party libraries as well as custom written javascript files inside of a React Component without loading them across other routes.
Kindly note am use a React Hook i.e. useEffect() thus you need a version of react which supports them.
Below is what worked for me.
import React, { useEffect } from "react";
const Airstrip = props => {
useEffect(() => {
// Now Attach All Third Party Scripts.
let threeJsScript = document.createElement("script");
let orbitJsScript = document.createElement("script");
let guiJsScript = document.createElement("script");
let mainJsScript = document.createElement("script");
// Hook Sources.
threeJsScript.src = `${process.env.PUBLIC_URL}/js/three.js`;
orbitJsScript.src = `${process.env.PUBLIC_URL}/js/orbit.js`;
guiJsScript.src = `${process.env.PUBLIC_URL}/js/gui.js`;
mainJsScript.src = `${process.env.PUBLIC_URL}/js/main.js`;
// Persist order of Loading.
threeJsScript.async = false;
orbitJsScript.async = false;
guiJsScript.async = false;
mainJsScript.async = false;
// Append to index.html
document.body.appendChild(threeJsScript);
document.body.appendChild(orbitJsScript);
document.body.appendChild(guiJsScript);
document.body.appendChild(mainJsScript);
// Do Clean ups
return () => {
document.body.removeChild(threeJsScript);
document.body.removeChild(orbitJsScript);
document.body.removeChild(guiJsScript);
document.body.removeChild(mainJsScript);
};
}, []);
return (
<div id="airstrip">
<canvas
id="canvas"
onClick={() => {
alert("Yeah");
}}
></canvas>
</div>
);
};
export default Airstrip;
Related
I want to import .ts, .tsx, .js, and .jsx files into a react component and render them within a PrismJS highlighting block. For example, let's say I have a TypeScript file with functionA in it that I want to highlight in my actual website:
functionA.ts:
export function functionA() {
console.log("I am function A!");
}
I want to include this in a different component. The problem is, when I import it, I am obviously importing the webpack module version of it. My weak attempt at trying to get my function render in a react component looks like this:
MyComponent.tsx:
import * as React from "react"
import { functionA } from "./functionA"
export function MyComponent() {
return (
<>
<h1>Here is your code block:</h1>
<pre>
<code>
{functionA.toString()}
</code>
</pre>
</>
)
}
and what will actually render on the page where the code block is will look something like this:
Here is your code block:
WEBPACK__IMPORT.functionA() {
console.log("I am function A!")
}
I can't exactly remember what the .toString() function output looked like, but the point is it is NOT just the contents of the file how it appears in a code edit for example - it has been modulized by WebPack.
So, in a Gatsby project, how can i get these various code snippets to be imported directly as a string, purely as they are written, without WebPack enacting its import stuff on it? Is there a plugin or some way to tell Webpack to use the imported file as its asset/source module type? I know for MD or MDX files there is the gatsby-remark-embed-snippet, but I am building a component based HTML page and can't use MD or MDX files!
It's very late, and perhaps I just can't see the forest from the trees, I know there must be a way to do this...
You need to require the file using webpack's raw-loader, i.e:
const functionA = require("!!raw-loader!./functionA");
This works for create-react-app, as in the solution discussed here, and this works for Gatsby as well!
After using require on such a file, the file contents can be rendered in the component as:
<pre>{functionA.default.toString()}</pre>
It's then up to you to add syntax highlighting using a tool like prism or similar.
Note this solution will only work as long as Gatsby V3 continues to use WebPack v4, as raw-loader is deprecated in WebPack v5 and will be phased out for asset/source type modules.
Hello.
I recently got stuck with pretty important thing on my gatsby site.
I have to import script from other site cause it is providing map widget. This is the widget from polish delivery company and it is only available under link https://mapa.ecommerce.poczta-polska.pl/widget/scripts/ppwidget.js.
It is activated by a function window.PPWidgetApp.toggleMap(). Problem is when i try to activate, html and css markup from widget are showing but map coming from js it is not.
Here is how I'm loading the script:
{
resolve: "gatsby-plugin-load-script",
options: {
src:
"https://mapa.ecommerce.poczta-polska.pl/widget/scripts/ppwidget.js",
},
},
When I'm on specific route where I'm using this widget and i refresh the page everything is working properly. So I'm guessing problem is that when this script is loaded in index it gets cached somehow by gatsby and most of the important features are not working. So can I load the script only when I'm on let's say route /delivery ? Or is there another, better way to load this script that may work fine ?
Link to github repo with this problem: https://github.com/Exanderal/gatsby-problem
The easiest, native and built-in way to achieve is using <Helmet> component. Basically, this component embeds everything that is inside in your <head> tag.
The problem using it is that if you need to activate or to wait for its loading to make some actions (like window.PPWidgetApp.toggleMap() in your case), it could be kind of buggy since sometimes it may load properly but sometimes not. I will show you different approaches to check which one fits you better.
<Helmet> approach:
<Helmet>
<script src="https://mapa.ecommerce.poczta-polska.pl/widget/scripts/ppwidget.js"/>
</Helmet>
As I said, this workaround may work for standalone scripts, but if you need to perform actions or wait for its loading it may not work. The next approach should fit you.
Custom script loading approach:
const addExternalScript = (url, callback) => {
const script = document.createElement('script');
script.src = url;
script.async=true;
script.onload = callback;
document.body.appendChild(script);
};
useEffect(()=>{
addExternalScript("https://mapa.ecommerce.poczta-polska.pl/widget/scripts/ppwidget.js",window.PPWidgetApp.toggleMap())
},[])
Basically, you are setting a custom function (addExternalScript) that creates the same script tag as the first approach and embeds the passed URL as a first function parameter. The second parameter is the callback function to trigger once it's loaded in the onload function.
Everything it's triggered in the useEffect function with empty deps ([]). The useEffect is a hook (available in React version ^16) that is triggered once the DOM tree is loaded, in this case, it's a nice way to ensure that the window object is properly loaded and set to avoid some common issues in Gatsby using global objects.
what are the various options to add an external JS library to reactjs component? for example, if we have any library file such as example.js and it is used by only one react component then i would like to either add this library at index.html or at the component level. I would like to know how to include at component level.
Hosted Client-Side Libraries
If you want to import a client-side hosted library your only option is to include it in index.html
For instance, if we wanted to include D3 in one of our components, we could add <script src="https://d3js.org/d3.v5.min.js"></script> to /public/index.html
Then to use it from inside your component, you need to call window.d3 instead of just d3
Non-Hosted client-side libraries
If you have a library that is not hosted, you could always save it to it's own file. once you do that, simply import it like you would any other local file import some_library from "./path/to/some/library"
By "external libraries" I'm assuming you're talking about npm/yarn repository libraries, for example:
$ npm install moment (Moment.js)
In those cases, you want to import it as:
import moment from 'moment'
Another example from the same package can be seen here
Easiest way is to find out whether your external library has an npm module, if so you can simply do an npm install.
Else you can add the link to this library in your index.html.
Otherwise you can download the files of your external library and use it inside as if its your own code(better checkout whether its free library to do that). In this case you can simply import the file
You can create a component that inserts the script to the header of your site. For example:
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
const ExternalLibrary = () => {
useEffect(()=>{
if (typeof window !== "undefined"){
// Make sure it only gets included once.
if (!document.getElementById("externalLibrary")){
const script = document.createElement('script');
script.id='externalLibrary'
script.type = 'text/javascript';
script.src = `https://externallibrary.com/example.js`;
// Call some function from the library when it loads.
script.onload = function() {
window.someFunction()
};
document.getElementsByTagName("head")[0].appendChild(script);
}
}
}, []);
return null;
}
export default ExternalLibrary;
Then you can just include this in your App.js, or other components.
You can use like that :
import abc from './abc.js'
Check here : How to include external javascript library in reactjs
If you have a parent component file that already imports React, why does any of its rendered children files also need to import React? Is it just a safety measure in case those children are ever rendered somewhere else where React has not been imported yet?
In nodejs each file is a module, that has its own scope of variables. When you import variable into file (React for example) you add this variable to the module scope, but not to the global scope.
In case of webpack you can use providePlugin to easily make React variable global:
new webpack.ProvidePlugin({
React: 'react' // ReactJS module name in node_modules folder
})
After that you are able to skip importing React variable in all of your modules. Webpack will do it itself where needed.
If you use JSX and babel, you have to import React in every file because babel-preset-react will transpile your JSX code into React.createElement() calls, so this code
const Foo = () => <h1>Test</h1>;
will be
var Foo = function Foo() {
return React.createElement(
"h1",
null,
"Test"
);
};
DEMO: Babel REPL
This is the reason why React should be accessible in the proper scope and one way to do it, is to import React in a file.
The root of the question is one of dependency management -- how do I, the author, describe and obtain external dependencies I need in my "thing" (application/component/module) for it to do its job?
JavaScript benefits (or suffers) from having a global namespace in which dependencies can be injected. While this can often simplify dependency management in the short term (e.g. you can ignore it and expect everything you need to be available in the global namespace), it can often cause issues as an application grows and changes. For example, given an application with multiple contributors, one might decide to change a part of the application to no longer use a particular dependency and therefore remove it. If another part of the application needed it but that dependency wasn't formally declared anywhere, it could be easily missed.
To do dependency management well, each discrete "thing" should describe its dependencies independent of any other "thing" such that it can be safely used in any given context. This ensures that each "thing" has exactly what it needs no matter how it is used and what its "parent" (the code importing the "thing") has.
An alternative to this approach is dependency injection. In this model, the "thing" provides an interface for passing the dependencies into the "thing" from the consumer. There are flexibility and testability advantages to this which are out of scope of your question. :)
// export a function that expects the React and PropTypes
// dependencies to be injected as parameters and returns
// the component rather than importing the dependencies
// and exporting the component
export default (React, PropTypes) => {
return class extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired
}
render () {
return (
<div />
);
}
};
};
All of that to say, it is somewhat a "safety measure" to have each "thing" import its own dependencies. It lets you safely refactor your code without the cognitive overhead of remembering what is providing those dependencies.
The reason is to avoid unnecessary compiled a JavaScript code that you don't have to compile jsx. For example, you have a file that have a function to do adding, or whatever function that doesn't need to compile jsx, then you don't put import React from 'react' on the top of that file. This will save you compile time.
I present you the following code:
https://plnkr.co/edit/xxNW1xAIPoGtTK84OxGq
NOTE: This does not work because of SystemJS which I just can not configure. Whoever wants can edit if freely to make it work. I have been using the angular-cli and default webpack config and this works.
My question is about the "AlertService" which is needed to display an "alert". It has been extracted in the core module. However when I need to use it I have import it like so import { AlertService } from '../core/alert/alert.service' as present in dashboard.component.ts in order to inject it.
Doesn't this break the modular approach since I have to give it the path to the class ? If I change the location of the AlertService within the CoreModule I still have to go and change the string in the DashboardComponent. Also in this example if AlertService is not present DashboardComponent will not fire ... but DashboardComponent is part of the DashboardModule which should be able to start on its own - otherwise what is the point of the modules if they are coupled statically I could just put everything in one place.
What I want is to create a general alert component which I only need to include and once in the whole app and be able to use it everywhere.
But I think I am misunderstanding the concept of modules and/or how to use them. I have read the Modules' section in Angular.io multiple times and gone through multiple tutorials.
Best regards
If having to change lots of file paths bothers you a lot, one possible solution is to add another layer - create a services.ts file, then have the canonical imports there rather than in each component. That way, you only have to update one file if you change the path:
services.ts:
import { AlertService } from './alert.service';
// ES2015 shorthand property initializer
export default {
AlertService
};
yourComponent.ts:
import { AlertService } from "../services";
...
If you want a component to work even if a service is not present, you can make it an optional dependency (returning null if it doesn't exist), but TypeScript forces you to still have to import the service into the file to get code completion/typings. That's always felt a little janky to me.