plotly / dash-component-boilerplate

Get started creating your own Dash components here.
273 stars 184 forks source link

Using external libraries #64

Open chschoenenberger opened 5 years ago

chschoenenberger commented 5 years ago

It seems to be impossible to import and work with external libraries in custom react components. I pinned the issue down on Webpack, which does not include the imported libraries in the final JS which is used by dash in Python.

Expected Behaviour

Code that runs fine using 'npm run start' can be converted to valid python code using 'npm run build:all'. 'python usage.py' can then be used to display the code using Dash.

Actual Behaviour

Code runs fine using 'npm run start'. It can be converted to a dash component, however on using this component calling 'python usage.py' the message: "Error loading dependencies" is shown. The browser console shows, that the component from the external library was not found (TypeError: Cannot read property 'Map' of undefined).

Steps to reproduce

  1. Run boilerplate and create example component.
  2. Import external library in custom component and call it in App.js
  3. Make sure it runs in 'npm run start'.
  4. Convert to python using 'npm run build:all'
  5. Call 'python usage.py'

Attached to this report you'll find the necessary files to reproduce the issue with the react-leaflet library. The files included are ['usage.py', 'webpack.config.js', 'App.js', 'index.js', 'DashLeaflet.js', 'index.js', 'package.json']. Make sure to install both react-leaflet and leaflet using npm. Furthermore, file-loader is needed to read an external css sheet for leaflet.

files_to_reproduce.zip

alexcjohnson commented 5 years ago

I haven't played with your example yet, but on the face of it, there are two ways to include external js, both of which we have examples of in dash-core-components. (see its webpack config here)

  1. Import the package without specifying it as external to webpack. This is done for example in Dropdown with react-virtualized-select - this way the external lib is included in the main bundle.

  2. Mark the external lib's entry point as a global, tell Webpack it's external, and include the file in _js_dist so it gets loaded in any page using this component. This is done by Graph with plotly.js: global entry, external in webpack config, and _js_dist (note that the external lib comes before the component bundle, which could be important if the bundle accesses it during script execution).

Thanks for bringing this up - let me know if one of those strategies works for you, then we should include more about this in the docs here. Usually the first strategy should suffice, but if for some reason you want to keep the external lib out of the bundle you can use the second.

chschoenenberger commented 5 years ago

Hi Alex Thanks for the fast reply. So I tried your approaches and this is what I found.

  1. This is what I actually tried first. However, doing this I get an "Uncaught TypeError: Object(...) is not a function". The stack trace points to some error in __webpack_require__. This is why I put it in the externals. (Probably) resulting from this, a second error is thrown: Error: dash_leaflet was not found.

  2. I'm not exactly sure if I did the right thing here: I added /* global react-leaflet:true */ to the top of my component JS file. If I try to get elements from react-leaflet (as in react-leaflet.Map) as you did in dash-core-components with Plotly, I get an error. If I additionally import the needed react components, add the package to the externals and adjust the _js_dist I still get the same error as before. I'm not sure however, if I adjusted the _js_dist properly. I first copied the react-leaflet module to dash_leaflet/node_modules (it is not in there inherently) and added { 'relative_package_path': 'node_modules\\react-leaflet\\dist\\react-leaflet.min.js', 'namespace': 'dash_leaflet' }, before the 'dash_leaflet.min.js' etc. I'm guessing I am doing something wrong there?

chschoenenberger commented 5 years ago

Update: I added 'react-leaflet.min.js' and 'react-leaflet.js' in the 'dash_leaflet' folder and added { 'relative_package_path': 'react-leaflet.min.js', 'namespace': package_name }, { 'relative_package_path': 'react-leaflet.js', 'namespace': package_name }, to _js_dist. I still get the 'Error loading dependencies' error and the console showing two errors:

Not sure if that's an issue from the react-leaflet library or dash though.

alexcjohnson commented 5 years ago

@chschoenenberger I tried using the files in your zip but wasn't able to get npm run start to work even. I'll try to take another look later, but I'd suggest tracking down the problem with the first approach is probably going to be the easiest, and best in the long run too.

If you do stick with the second approach though, don't make separate entries for the dev and minified bundles. Use relative_package_path and dev_package_path in the same entry. I don't know what's causing your latest error, but that's why you have two matching errors (with React in the second minified to l in the first)

chschoenenberger commented 5 years ago

@alexcjohnson I created a repo so you can reproduce it properly. This repo is adjusted as you suggested first with the package not specified as external. You can find it here. I'll have another look as well and will let you know if I find something.

chschoenenberger commented 5 years ago

@alexcjohnson So we got it working with the second approach. We had to do some additional stuff which was probably related to the react-leaflet library.

  1. Adjust files as mentioned in your comment (Global entry of react-leaflet in react component, reference library as external in webpack config, adjusted _js_dist and _css_dist)
  2. Copy all code from leaflet.js and paste it at the beginning of react-leaflet.js file.
  3. Add the newly created react-leaflet.js file to the folder containing the python file of the component.

With this steps, the component works in a python environment. However, it won't run in npm, as it is unable to find the global entry of react-leaflet. I pushed the changes to the repo from last time (here). Some of the issue is likely to be from the react leaflet library. However, I think there is some issue in the inclusion of external libraries when working with the boilerplate you might want to look into.

I'll leave it to you if you want to close the issue or leave it open for now. Thanks for the help.

Michael-fore commented 4 years ago

are there separate steps for react packages that use jquery? or should it work off the shelf?