2

I'm rebuilding the Atlassian React+Flux tutorial in TypeScript (using bindings of React, Flux and Node from tsd) but I'm encountering a problem with implementing the dispatcher, which essentially is a singleton.

I'm trying the following:

AppDispatcher.ts

export var instance = AppDispatcher.getInstance();
class AppDispatcher extends Flux.Dispatcher<AppPayload> {

    private static _instance:AppDispatcher = new AppDispatcher();

    constructor() {
        super()
        if(AppDispatcher._instance){
            throw new Error("Error: Using this constructor should not be possible...WTH javascript");
        }
        AppDispatcher._instance = this;
    }

    public static getInstance():AppDispatcher
    {
        return AppDispatcher._instance;
    }
...
}

Like in the tutorial I use a JSON file on localStorage to fake a web API. So my ActionCreator does the following:

ActionCreator.ts

import AppDispatcherInstance = require('../dispatcher/AppDispatcher');

//ActionCreator
//In Flux ActionCreators
export function receiveAll(rawNodes:any) {
    AppDispatcherInstance.instance.handleServerAction(AppDispatcherInstance.ActionIdentifier.REFRESH_FROM_SERVER, rawNodes)
}

Unfortunately I get a runtime exception Uncaught TypeError: Cannot read property 'getInstance' of undefined pointing to the transpiled Javascript line

exports.instance = AppDispatcher.getInstance();
  1. Am I implementing the singleton wrong in TypeScript?
  2. Is there a better way in general to do this? I thought about not writing a class at all and to the singleton by only exporting certain functions (like recommended in other SO questions) but I still want to extend the Flux.Dispatcher class?
Shady
  • 794
  • 2
  • 8
  • 23

2 Answers2

2

Here's how I've been using singletons with modules in a React/Flux project:

class AppDispatcher extends Flux.Dispatcher {
    // ...
}

export default new AppDispatcher();

The singleton can be imported in another file like:

import AppDispatcher from "./AppDispatcher";
AppDispatcher.dispatch(...);

If you need the class type exported, you can add export to class AppDispatcher and import the singleton by a different name if you need both in the same file (I tend to need this in unit tests):

import AppDispatcherInstance, {AppDispatcher} from "./AppDispatcher"

Aaron Beall
  • 49,769
  • 26
  • 85
  • 103
0

As the commentor suggests, you need to move code that consumes a class below the declaration of that class. Classes are just variables, and variables in JavaScript get initialized in order.

In general, you shouldn't need to use the explicit class singleton pattern in TypeScript. See How to define Singleton in TypeScript

Useless singleton pattern

class Singleton {
    /* ... lots of singleton logic ... */
    public someMethod() { ... }
}

// Using
var x = Singleton.getInstance();
x.someMethod();

Namespace equivalent

namespace Singleton {
    export function someMethod() { ... }
}
// Usage
Singleton.someMethod();
var x = Singleton; // If you need to alias it for some reason
Community
  • 1
  • 1
Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
  • Thanks guys, you were right. @ryan-cavanaugh one general question: Say I use EventEmitter of node.d.ts. I would construct a single instance of it as a non-exported variable in a .ts. Then I implement functions like emitChange(), which use this single instance, and I export only those. Is this the right way to go? – Shady Nov 06 '15 at 07:46