d3 / d3-interpolate

Interpolate numbers, colors, strings, arrays, objects, whatever!
https://d3js.org/d3-interpolate
ISC License
494 stars 69 forks source link

Recommended way to use D3+plugins with ES6-style imports? #45

Closed dzackgarza closed 7 years ago

dzackgarza commented 7 years ago

I have a specific case in mind I'm wondering about, related to this plugin, but I'm also wondering how this should be done in general.

It seems like most plugins attach functions to the d3 object itself, so for this plugin my first approach was something like this:

import * as d3 from 'd3';
import 'd3-interpolate';
window.d3 = d3;

The specific issue I'm running into here is that later I have code like

const someFunction = (arr, numSamples) => {
        const interpolator = d3.interpolateRgb(arr[0], arr[1]);
        return d3.quantize(interpolator, numSamples);
    };

which throws the error "d3.quantize is not a function". Placing a debugger to examine the d3 object, it looks like d3.interpolateRgb exists, along with many of the other d3.interpolate functions, but d3.quantize doesn't.

One quick fix was to do something like

import * as interpolate from 'd3-interpolate';
d3.quantize = interpolate.quantize

but I'm just wondering what the proper approach might be for importing/attaching plugin functions (or if this is just an isolated instance!)

mbostock commented 7 years ago

It looks like you’re trying to use the D3 modules introduced in D3 4.0 with the old D3 3.x bundle. D3 3.x is no longer supported, and I would strongly recommend against mixing D3 modules with D3 3.x; you should switch to using only the D3 modules.

As of D3 4.0, none of the D3 modules (formerly known as plugins) rely on a global d3 object. Each module is available either as a UMD bundle for use in Node or browsers, and as ES modules for use in bundlers and other modern JavaScript environments.

Importing a D3 module without declaring which symbols you want to use typically has no effect, so this doesn’t do anything:

import "d3-interpolate";

An exception to that is the d3-transition module, which extends the selection.prototype to add selection.transition and selection.interrupt.

To use a D3 module, then, you should import the symbols you want to use. For example to compute an array of ten colors strings that interpolate from red to blue in RGB color space:

import {interpolateRgb, quantize} from "d3-interpolate";

let colors = quantize(interpolateRgb("red", "blue"), 10);

Generally speaking you should not using the d3 default bundle in conjunction with the D3 modules. For example, importing d3-interpolate along with d3 is redundant: the d3 default bundle includes everything from d3-interpolate already. When used in conjunction with import * as d3 from "d3", there is no (easy) way to extend the created d3 object with D3 modules that are not included in the default bundle, such as d3-scale-chromatic or d3-geo-projection. Also, using the import * syntax is less amenable to static analysis in conjunction with tree shaking.

If you have further questions, please use Stack Overflow tag d3.js to ask for help. Although I make an effort to assist everyone that asks, I am not always available to provide help promptly or directly. Stack Overflow provides a better collaborative forum for self-help: tens of thousands of D3-related questions have already been asked there, and some answered questions may be relevant to you.

When asking for help, please include a link to a live example that demonstrates the issue, preferably on bl.ocks.org. It is often impossible to debug from code snippets alone. Isolate the issue and reduce your code as much as possible before asking for help. The less code you post, the easier it is for someone to debug, and the more likely you are to get a helpful response.

If you have a question about D3’s behavior and want to discuss it with other users, also consider the d3-js Google Group or joining the d3-js Slack.

Thank you! 🤗