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
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.
I'm trying to figure out how I can use some existing functions in my React app.
There is a javascript file called Foo.js and and associated typing Foo.d.ts
Foo.js has many functions and at the top I see
//I don't understand what the below lines re doing
var A = A || {};
A.B= A.B|| {};
A.B.C= A.B.C|| {};
A.B.C.Foo= A.B.C.Foo|| {};
(function ()
//here there are many functions
)();
Then in the d.ts file
declare namespace A.B.C.Foo
{
//here there are exported functions
}
Now I have my react components in another folder, and I wish to use the functions defined in the Foo.js file. How do I use them?
I've tried adding the below to my component
///<reference path="../../../typings/#Test/Foo.d.ts" />
My component looks like below
const Component: React.FunctionComponent<IProps> = (props:Iprops) =>
{
//I'm trying to use the function here.
}
What do I need to do to import the functions? I have no idea how to proceed.
Edit: One more piece of information:
In the html file I have a reference to Foo.js added in a script tag
" />
Edit: So I was able to get this to work. The accepted answer pointed me in the right direction with using global. Looks like Foo.js was already declared in a manner that the functions were available in React already.
On the react side, adding the below reference path worked. To get type checking what I was missing was that that the .d.ts file exports a namespace so I had to prefix all functions with that namespace like so: A.B.C.Foo.FunctionName()
///
The best way to expose functions to the other modules of your application is when a module provides a export statement:
// Foo.js
export const mySpecialFunction = () => {}
// otherFile.js
import { mySpecialFunction } from './Foo.js';
mySpecialFunction();
Otherwise, you will need to rely on window (considering your code runs on browser) to expose you function globally by import Foo.js somewhere (preferably on your app's entrypoint) and using the function:
// Foo.js
window.mySpecialFunction = () => {}
// index.js (entrypoint of your app)
import './Foo.js';
// otherFile.js
mySpecialFunction();
But about Typescript, you can't just define a namespace. It must match with a path alias you define on tsconfig.js which points to Foo.js.
The fact is that isn't clear what you are aiming.
Edit:
How would I do this "typescript as a global module attached to window"?
You could follow this documentation https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-d-ts.html. It will help you to define a global function or namespace.
Disclaimer: In a general way, do not rely on global modules as they decrease the code readibility.
Normally for a well formed library with exported functions you should add:
import FooFunctions from "A.B.C.Foo"
in your component, and then call the functions with FooFunction.{theFunctionYouWantToCall}(). You can also use this format of import:
import {theMatchingFunctionYouWantToCall} from "A.B.C.Foo"
Note: that's the normal scenario if you imported the library from npm, and it is in your node_modules. If you created this library or created the missing .d.ts file for it, make sure that Typescript is finding your .d.ts first.
I have a container in react-native that is importing the typical kind of function that we usually store at utils directory such as capitaliseWord() or whatever.
One of the functions of that utils module uses t, so we import i18n inside that utils folder in order to be able to use t
We use languageDetector inside our i18n to detect the user's mobile language. Because languageDetector needs deviceLocale (e.g UK, US, NL, etc), and Jest runs on a different context, if I try to do test the container, I will get Cannot read property 'deviceLocale' of undefined.
So I created a manual __mocks__ directory (as https://jestjs.io/docs/en/manual-mocks#mocking-user-modules states) and I created my own i18n that just injects the deviceLocale string manually, in order to be able to run the tests.
Turns out the test ignores the __mocks__/i18n and points directly to the original one... Any ideas of what it could go wrong?
And my jest config inside package.json
https://gist.github.com/e00dd4ee41b06265632d3bcfe76e7cb0
original i18n.js
https://gist.github.com/pavilion/3c48c6017a82356914f0ad69d7251496
mocked i18n.js
https://gist.github.com/pavilion/17e322340581fb948ed7e319ae4a5ac9 (notice the detect key inside languageDetector has been manually mocked)
Component to be tested
https://gist.github.com/pavilion/20dc0c5b1a6d2ee709b7a71ec7b819c1
utils.js
https://gist.github.com/pavilion/1c5df0f71d50462d75234365ae1e4aaf
Comp.test.js
https://gist.github.com/pavilion/2a84880bee3a99fa51fc3e28cfa8f913
Error:
https://gist.github.com/pavilion/012ee0889ebbe2b93b2108d93543e19c
I think the problem is not in the mock, but the import! Try this time requiring the component in the test, after the mock has been injected:
import React from 'react';
import { shallow } from 'enzyme';
jest.mock('../../config/i18n');
describe('<Comp />', () => {
it('must match the snapshot', () => {
// Require here instead of importing on top
const Comp = require("./Comp").default;
// state and props properly set up
const wrapper = shallow(<Comp />);
expect(wrapper).toMatchSnapshot();
});
});
I tried this locally and it's working fine: the module gets mocked. I simplified a lot my example so perhaps you run into some new error but technically this should solve your problem.
EDIT: pushed my working example to github here in case it's for any help.
I am using a very large library in my spfx solution and I would like to not only share it among webparts within this app but share it across any other webparts that might be using this library.
What I know
I know that you can include external libraries such as jquery and some libraries that have typings separate from their npm package.
I know that even if I put a reference to an external library in the externals section it won't actually pull down the library if I don't import it anywhere in my code.
I understand the current examples in the documentation. I managed to successfully use moment-timezone which is great.
Problem I am having
I am trying to use the DevExtreme and devextreme-react libraries. This library is very large so I want to include it once from a cdn when a webpart is requesting it and continue using the same script with each additional webpart instance. The problem is that the library comes with typings included in it. It is in a subfolder bundles/dx.all.d.ts instead of in its own package. It also has multiple files you can include instead of including the entire library. In addition the devextreme-react is depending on the devextreme library so it is also including files from devextreme.
The project either doesn't load the external libraries because I don't include them, or things fail to compile because I am not importing it properly..? maybe?
My question is how do I exclude the library, add it to externals configuration, import it into my project files with typings and still have my project compile?
So I found a potential solution, of course the only problem is I have to stop using devextreme-react and use just plain jquery.
Basically if I put in the config this:
"jquery": {
"path": "https://code.jquery.com/jquery-2.1.1.min.js",
"globalName": "jQuery"
},
"devextreme/bundles/dx.all": {
"path": "https://cdn3.devexpress.com/jslib/18.1.6/js/dx.all.js",
"globalName": "DevExpress",
"globalDependencies": ["jquery"]
}
And then anywhere that requires a devextreme component I would have to create a wrapper and import devextreme like this:
import * as React from 'react';
import * as $ from 'jquery';
import DevExpress from 'devextreme/bundles/dx.all'; DevExpress;
export default class DxButton extends React.Component<DevExpress.ui.dxButtonOptions> {
private el: HTMLElement;
private $el: JQuery<HTMLElement>;
public constructor(props: DevExpress.ui.dxButtonOptions) {
super(props);
}
public componentDidMount() {
this.$el = $(this.el);
(this.$el as any).dxButton({
...this.props as any
} as DevExpress.ui.dxButtonOptions);
}
public componentWillUnmount() {
(this.$el as any).dxButton('dispose');
this.$el.remove();
}
public render(): React.ReactElement<DevExpress.ui.dxButtonOptions> {
return <div ref={el => this.el = el} />;
}
}
The reason I can't use devextreme-react is because it uses the modules of devextreme and forces the package to be bundled no matter what.
Not the ideal solution but seems like the only possible way to do it in the end.
Be aware to not use imports from the devextreme-react as straight forward.
//wrong import
import { TextBox } from 'devextreme-react';
You must specify import for each component you have.
//Correct import
import { TextBox } from 'devextreme-react/text-box';
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;