Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting Singleton JS objects to use ES6 classes

I'm using ES6 with the Webpack es6-transpiler per my article here: http://www.railsonmaui.com/blog/2014/10/02/integrating-webpack-and-the-es6-transpiler-into-an-existing-rails-project/

Does it make any sense to convert two Singleton objects to use ES6 Classes?

import { CHANGE_EVENT } from "../constants/Constants";

var EventEmitter = require('events').EventEmitter;
var merge = require('react/lib/merge');

var _flash = null;

var BaseStore = merge(EventEmitter.prototype, {

  emitChange: function() {
    this.emit(CHANGE_EVENT);
  },

  /**
   * @param {function} callback
   */
  addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
  },

  /**
   * @param {function} callback
   */
  removeChangeListener: function(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  },

  getFlash: function() {
    return _flash;
  },

  setFlash: function(flash) {
    _flash = flash;
  }
});

export { BaseStore };

This is file ManagerProducts.jsx that has a singleton that should extend from BaseStore.

/**
 * Client side store of the manager_product resource
 */
import { BaseStore } from "./BaseStore";
import { AppDispatcher } from '../dispatcher/AppDispatcher';
import { ActionTypes } from '../constants/Constants';
import { WebAPIUtils } from '../utils/WebAPIUtils';
import { Util } from "../utils/Util";
var merge = require('react/lib/merge');

var _managerProducts = [];

var receiveAllDataError = function(action) {
  console.log("receiveAllDataError %j", action);
  WebAPIUtils.logAjaxError(action.xhr, action.status, action.err);
};

var ManagerProductStore = merge(BaseStore, {
  getAll: function() {
    return _managerProducts;
  }
});

var receiveAllDataSuccess = function(action) {
  _managerProducts = action.data.managerProducts;
  //ManagerProductStore.setFlash({ message: "Manager Product data loaded"});
};


ManagerProductStore.dispatchToken = AppDispatcher.register(function(payload) {
  var action = payload.action;
  if (Util.blank(action.type)) { throw `Invalid action, payload ${JSON.stringify(payload)}`; }

  switch(action.type) {
    case ActionTypes.RECEIVE_ALL_DATA_SUCCESS:
      receiveAllDataSuccess(action);
      break;
    case ActionTypes.RECEIVE_ALL_DATA_ERROR:
      receiveAllDataError(action);
      break;
    default:
      return true;
  }
  ManagerProductStore.emitChange();
  return true;
});

export { ManagerProductStore };
like image 254
justingordon Avatar asked Oct 05 '14 17:10

justingordon


2 Answers

No. Makes no sense.

Here's a really simple example of a singleton object in es6:

let appState = {};
export default appState;

If you really want to use a class in your singleton approach, I would recommend against using "static" as it more confusing than good for a singleton at least for JS and instead return the instance of the class as a singleton like so...

class SomeClassUsedOnlyAsASingleton {
  // implementation
}

export default new SomeClassUsedOnlyAsASingleton();

This way you can still use all the class things you like that JavaScript offers but it will reduce the confusion as IMO static isn't fully supported in JavaScript classes anyway as it is in typed languages such as c# or Java as it only supports static methods unless you just fake it and attach them directly to a class (at the time of this writing).

like image 95
King Friday Avatar answered Oct 14 '22 13:10

King Friday


I'd argue that singletons (classes that manage their own singleton lifetime) are unnecessary in any language. That is not to say that singleton lifetime is not useful, just that I prefer that something other than the class manage the lifetime of an object, like a DI container.

That being said, the singleton pattern CAN be applied to JavaScript classes, borrowing the "SingletonEnforcer" pattern that was used in ActionScript. I can see wanting to do something like this when porting an existing code base that uses singletons into ES6.

In this case, the idea is that you make a private (via an un exposed Symbol) static singleton instance, with a public static instance getter. You then restrict the constructor to something that has access to a special singletonEnforcer symbol that is not exposed outside of the module. That way, the constructor fails if anyone other than the singleton tries to "new" it up. It would look something like this:

const singleton = Symbol();
const singletonEnforcer = Symbol()

class SingletonTest {

  constructor(enforcer) {
    if(enforcer != singletonEnforcer) throw "Cannot construct singleton";
  }

  static get instance() {
    if(!this[singleton]) {
      this[singleton] = new SingletonTest(singletonEnforcer);
    }
    return this[singleton];
  }
}

export default SingletonTest

Then you can use it like any other singleton:

import SingletonTest from 'singleton-test';
const instance = SingletonTest.instance;
like image 40
Brian Genisio Avatar answered Oct 14 '22 13:10

Brian Genisio