Debugging React components

Hey Tom - I’m working on a React component that I’m trying to get to work with Observable (see notebook).

I followed your stubborn modules tutorial and tried the the module debugger, however I’m running into a couple issues getting it all to play nice.

React is throwing an error that the element refs in Kepler are broken:

I tried to chase down the issue but a) it’s hard to debug the minified React code, and b) I’m inclined to think that the problem is issue number 3; there’s probably an extra React being loaded somewhere.

So I started going down this rabbit hole of trying to load the debug versions of React and React-DOM, but bundle.run doesn’t support serving the dependencies unminified, and when I try to use the development versions from unpkg either Kepler or React-DOM seems to want to load a second version of React (using a different url due to a version mismatch between the ^16.0.0 that React-DOM wants and 16.4.0 that I’ve explicitly loaded). This new version wants to check for a global process.env, and also wants a global module / require which are incompatible with Observable/d3-require’s require.

This seems to be causing more trouble than it’s worth but I’d appreciate any tips and tricks you might have in solving this. Thanks for an otherwise awesome product!

This one’s definitely a boss-mode issue… so it’s a bit multi-layered.

  1. Unfortunately, React and ReactDOM don’t provide an unpkg field or a module field. They’re unusually difficult to load in any kind of UMD environment, and we’ve got a thread ongoing to see if they can be a little less difficult. This is why modules with UMD builds that depend on ‘react’ always fail - they try to load whatever React’s entry point is, hoping that it’s UMD, and it isn’t: it’s CommonJS, meant for Node. This is even though React does have a UMD dist - it just doesn’t mention it in its package.json, so it’s impossible to load without some manual effort.
  2. Kepler requires React, but doesn’t have any sort of browser build. So the one route to including it in a browser environment is going through bundle.run (or wzrd.in, or another bundler service), and all those bundlers will look at its dependencies and install React, and bundle a copy of React so that all those import * from 'react' or require('react') calls work. So, yep, two copies of React.

But let’s look to the future. I can see three solutions to this, which fit into short & medium terms:

  1. If Kepler.gl included a UMD build and pointed to it with an unpkg field in its package.json, then the same dirty trick I used to load Semiotic would work for Kepler.gl, and then if/when the React team gives React a browser-compatible entry point or the Observable team decides to hardcode a React workaround into the require() function itself, requiring Kepler.gl will just work.
  2. Even shorter term than that would be creating a small module that depends on and requires both Kepler.gl and React and exports both of their exports, thus solving the ‘two Reacts’ problem. We’ve done this before - created small stubs - for other modules, like vega, to coax them into working better with Observable.
  3. Longer-term, if the React team decides they really don’t want to point to their UMD builds, but they do want to support fancy new ES6 modules and add a module field to their package.json, and if the Kepler.gl team does the same, then the two will be able to play nicely together using dynamic import() syntax, or using bundle.run.

So - unfortunately no quick fix that I can see at this point (Mike or Jeremy might have ideas tomorrow), but there are some routes to making everything play nicely together.

-Tom

Really appreciate the fast response! That req tip was super helpful.

and bundle a copy of React so that all those import * from ‘react’ or require(‘react’) calls work. So, yep, two copies of React.

That’s a bummer. If I’m reading the d3-require source right the only global it exposes is define, so the Kepler library code needs to be AMD-aware to take advantage of Observable’s cached modules, right?

Unfortunately we’ve built the library for commonjs environments and don’t have any plans to support AMD. I gave it a shot today to add rollup but conflicts with babel plugins mean it’s more headache than it’s worth for me to add this to the build system at the moment. I’d be open to re-exploring in the future, or if there’s a low-stakes way of working with one of those third party services to take our amd code and convert it I’m all ears.

You also mentioned a manual route. Is this something feasible to do right now in Observable? I didn’t see an obvious way to modify the internal modules map, but I’m envisioning something similar to the following:

React = req('react')
-----
ReactDom = req('react-dom')
-----
Kepler = {
  await React
  await ReactDom
  req.define('react', React)
  req.define('react-dom', ReactDom)
  return req('kepler')
}

Where req.define would expose the correct react and react-dom for anything using the module loader.

Even shorter term than that would be creating a small module that depends on and requires both Kepler.gl and React and exports both of their exports, thus solving the ‘two Reacts’ problem. We’ve done this before - created small stubs - for other modules, like vega, to coax them into working better with Observable.

Got an example of this? I’d love to see a working version.

I tried to add my own with AMD, but still no dice.

Thanks!