2

I'm trying to use an ES5 module in a new ReactJS application and I'm struggling to understand how to correctly import that module, such that the main function within it can be found and executed.

I'm loading the module; import 'air-datepicker';

I know I'm doing something wrong here and that it's not as simple as this, for an old library that doesn't have proper exports!

Anyway, then I should be able to manually initialise a date picker using an existing div like this;

$('#myDiv').datepicker();

I've tried multiple variations of the import and require, but I'm always getting the same error - 'datepicker is not a function'.

The library I'm experimenting with is air-datepicker. I've installed the module using npm without problems and I know the library works perfectly without React on a simple page loading the script manually in a script tag. My ReactJS app is a basic template created using 'create-react-app', from the FB tutorial pages.

Ben Logan
  • 166
  • 2
  • 9
  • Did you install it using `npm install --save air-datepicker`? or added a `` in your html? – jpdelatorre Nov 24 '16 at 22:09
  • npm install. Via script tags originally in a simple demo page just to prove it works! – Ben Logan Nov 24 '16 at 22:29
  • Are you using webpack? Also, looks like air-datepicker uses jquery, do you have jquery loaded too? – jpdelatorre Nov 24 '16 at 22:31
  • I believe the react template project is bundled with webpack, yes. jQuery is loaded, yes. I've tested it and used it to modify the div in question so I know that's working OK. I also get a separate compile time error from the datepicker import if I take out jQuery. – Ben Logan Nov 24 '16 at 22:35
  • How did you add your jQuery? `npm install` or ` – jpdelatorre Nov 24 '16 at 22:44
  • It seems that `jQuery` is not available when plugin is being defined. `jQuery` should be included and added to `window` to make it available in global context. I think @jpdelatorre answer is correct. Perhaps only you don't need to import `$` and `jQuery` both, single `jQuery` is enough. – t1m0n Nov 25 '16 at 08:41
  • Thanks for the comments. I can get it working when jQuery is added via a – Ben Logan Nov 27 '16 at 20:20

3 Answers3

2

If you're using create-react-app, you should be able to import it like

import 'air-datepicker/dist/css/datepicker.min.css';
import 'air-datepicker';

If you added your jQuery using <script> tag in your HTML, you need to add this line before the air-datepicker imports

const $ = window.jQuery;
const jQuery = window.jQuery;

If you added jQuery using npm install, you'll have to add these following lines

import $ from 'jquery';
import jQuery from 'jquery';
window.$ = $;
window.jQuery = jQuery;
//... air-datepicker imports

Make sure to initialize it inside your componentDidMount or somewhere you're sure that the element has been mounted already.

componentDidMount() {
    $('#my-element').datepicker();
}

render() {
    return <div>
        <input 
            id="my-element" 
            type='text' 
            className="datepicker-here" 
            data-position="right top" />
    </div>
}
jpdelatorre
  • 3,573
  • 1
  • 20
  • 25
  • When jQuery is added via a – Ben Logan Nov 27 '16 at 20:29
  • You're right. Looks like the main jquery file in the `dist` folder doesn't work well with npm. Try importing jquery from the `src` folder. `import $ from 'jquery/src/jquery';` – jpdelatorre Nov 27 '16 at 20:48
  • So, I think it's actually just that the window.$ isn't happening in the order that I'd expect it to, when the code is translated. I have it above the import of air-datepicker, but I can see at runtime that it has moved to AFTER the import of air-datpicker; window.$ = _jquery2.default; If I pause execution just before the import of air-datepicker and then manually assign the global context in the console (using the above js snippet), then it works absolutely fine. Why is the execution of the window. statements pushed to below all of the imports, how can I prevent that from happening. Webpack? – Ben Logan Nov 27 '16 at 21:00
  • On my test, jquery doesn't load at all using `import $ from 'jquery';` Only `import $ from 'jquery/src/jquery';`. Did it not work for you? – jpdelatorre Nov 27 '16 at 21:09
  • Pretty sure the original jQuery import is working just fine, as I say, it's the ordering of the window. assignment that I think is causing the problems. Importing from /src works too for the local context, it's no different for me. – Ben Logan Nov 28 '16 at 08:07
2

Well, that's a day of my life that I'm never getting back!

The problem was caused by Babel import ordering. Import statements are hoisted - meaning they are evaluated first, before any other code (i.e. my window. assignments) are executed. This is 'by design' in babel.

The only way to avoid Babel hoisting, from what I can tell, is to avoid 'import' altogether and use require instead.

So, in the following order the global $ will have been set when you come to require air-datepicker. If you try to 'import' air-datepicker it won't work because Babel will evaluate all of your import statements before executing the window. assignment.

import $ from 'jquery';
window.$ = $;
require('air-datepicker');

There are one or two other approaches that also would have worked, but they are all less desirable because they need you to manually configure webpack - i.e. 'ejecting' the create-react-app config and going it alone...

Use the imports-loader;

// only works if you disable no-webpack-loader-syntax
require("imports?$=jquery!air-datepicker");

or, use the ProvidePlugin, making the module available for all modules.

Ben Logan
  • 166
  • 2
  • 9
0

You have to give it an identifier:

import datepicker from 'air-datepicker';
curv
  • 3,796
  • 4
  • 33
  • 48