I'm working on refactoring some existing code to be a bit more extensible and am wanting to create a generic factory API for starting D3 charts.

Currently, my factory looks like this:

import { select as d3Select } from 'd3';

const protoChart = {
  width: window.innerWidth,
  height: window.innerHeight,
  margin: {
    left: 10,
    right: 10,
    top: 10,
    bottom: 10,

export default function chartFactory(opts, proto = protoChart) {
  const chart = Object.assign({}, proto);
    .forEach(([key, val]) => {
      chart[key] = Object(val) === val ? Object.assign({}, chart[key], val) : val;

  chart.svg = d3Select('body')
    .attr('id', chart.id || 'chart')
    .attr('width', chart.width - chart.margin.right)
    .attr('height', chart.height - chart.margin.bottom);

  chart.container = chart.svg.append('g')
    .attr('id', 'container')
    .attr('transform', `translate(${chart.margin.left}, ${chart.margin.top})`);

  return chart;

Which I can then instantiate with:

const chart = chartFactory({
  margin: {
    left: 20,
  id: 'wooo',

This allows me to override the all the top-level properties with objects, meaning I can set the left margin and nothing else when I instantiate.

My questions:

  • Is there a better way of assigning my factory arguments to the prototype? I feel like I'm missing something here, though it could just be due to the fact I'm doing a semi-deep merge.

  • Am I instantiating the prototype within my factory correctly? Or will I have problems with instance safety if I create multiple charts?

  • Is there a better pattern I should be using for a functional programming-inspired library like D3?


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.