Why would you need to null coalesce a static member initialization? - javascript

I'm looking through the Mixpanel for React Native example project and they're initializing a singleton like this:
import {Mixpanel} from 'mixpanel-react-native';
import {token as MixpanelToken} from './app.json';
export default class MixpanelManager {
static sharedInstance = MixpanelManager.sharedInstance || new MixpanelManager();
constructor() {
this.configMixpanel();
}
configMixpanel = async () => {
this.mixpanel = await Mixpanel.init(MixpanelToken);
}
}
I've never seen a singleton handled quite this way. What's the circumstance where MixpanelManager.sharedInstance will already be set when this class gets declared?

Related

ES6 circular dependency. Implementing dependency hub

I found a lot of articles about circular dependencies and that it indicates the design/architecture flaws in the project. In most of the cases it can be fixed easily by slightly refactoring your classes.
In this example I'm trying to create a hub for dependencies. This is a library of classes that can be imported by different projects. The goal is to have getters in the main class that will return an instance of the dependency. This instance has to be a singleton and it's stored in the main class' dependencies that next time if someone calls a getter - it will return the same instance of that class.
Here is how code looks like:
// deps
import { DepA } from './dep-a';
import { DepB } from './dep-b';
const depsKey = '__MY_DEPS__';
class Deps {
dependencies = {};
get depA() {
return this.getDependency('depA', DepA);
}
get depB() {
return this.getDependency('depB', DepB);
}
bind(key, value) {
this.dependencies[key] = value;
}
getDependency(key, serviceClass) {
let service = this.dependencies[key];
if (!service && !!serviceClass) {
// if instance is not created yet, we instantiate the class and put instance in the dependencies
service = new serviceClass();
this.bind(key, service);
}
return service;
}
}
export const deps = (() => {
window[depsKey] = window[depsKey] || new Deps();
return window[depsKey];
})();
// dep-a
import { deps } from './deps';
export class DepA {
methodA() {
console.log(deps.depB);
}
}
// dep-b
import { deps } from './deps';
export class DepB {
methodB() {
console.log(deps.depA);
}
}
As you can see - this creates a circular dependency problem, since class Deps uses classes DepA and DepB in its getters to create an instance of those classes if it doesn't exist. And classes DepA and DepB use an instance of Deps to retrieve each other via its getters.
I hope this explanation is not very cumbersome.
Can anybody suggest the changes I need to make to get rid of the circular dependency here but to keep an idea of accessing singletons via Deps class (deps instance)?
My recommendation would be this:
a-singleton.js
import { DepA } from './dep-a';
let singleton;
export function getA() {
if (!singleton) {
singleton = new DepA();
}
return singleton;
}
b-singleton.js
import { DepB } from './dep-b';
let singleton;
export function getB() {
if (!singleton) {
singleton = new DepB();
}
return singleton;
}
then wherever you need these singletons, you can import the file and call the function to get the singleton.
Depending on what these classes do and how/when they reference eachother, you may also be able to directly do export default new DepA(), but depending on how they reference eachother, and how much they need to do at instantiation time, the lazy approach I've shown here can be better or necessary.

Using static in page object (Cypress)

Looking through various examples of implementing e2e tests on Cypress, I came across the fact that many people use the method of creating a new object, instead of using static. Why do they do this? Why not use static for page-object methods, because we don't change any data in the class itself and, accordingly, don't communicate to this, and we don't need to have multiple instances of the same page (or I don't see a scenario for using this). I understand that Selenium uses page factory and because of this it is necessary to create a new object, but I did not find an analogy in Cypress.
Example of creating a new object:
import { BasePage } from './BasePageClass'
import { navMenu } from './NavigationMenuClass';
import { queryPage } from './QueryPageClass';
export class MainPage extends BasePage {
constructor() {
super();
this.mainElement = 'body > .banner';
}
verifyElements() {
super.verifyElements();
cy.get(this.mainElement).find('.container h1').should('be.visible');
}
switchToQueryingPage() {
navMenu.switchToQueryingPage();
queryPage.verifyElements();
}
};
export const mainPage = new MainPage();
Example using static:
import { BasePage } from './BasePageClass'
import { navMenu } from './NavigationMenuClass';
import { queryPage } from './QueryPageClass';
export default class MainPage extends BasePage {
static mainElement = 'body > .banner';
constructor() {
super();
}
static verifyElements() {
super.verifyElements();
cy.get(MainPage.mainElement).find('.container h1').should('be.visible');
}
static switchToQueryingPage() {
navMenu.switchToQueryingPage();
queryPage.verifyElements();
}
};
Here is an example of a composition of commands my seniors have taught me to use. I have a function buyProduct, but it requires several steps and I have made them in different commands that can be used in different cases:
it('buyProductAsCustomer', ()=>{
cy.login(customer)
cy.shopFe_openProductByName(product.name)
cy.shopFe_addProductToCart()
cy.shopFe_openCheckoutFromCart()
cy.shopFe_selectPaymentMethod("payWithCreditCardMethod")
cy.shopFe_fillCheckoutFormAsCustomer(customer)
cy.shopFe_checkoutPageGetOrderData()
cy.shopFe_submitCheckoutForm()
cy.shopFe_completePaymentWithCreditCard()
});
it('buyProductAsGuest', ()=>{
cy.shopFe_openProductByName(product.name)
cy.shopFe_addProductToCart()
cy.shopFe_openCheckoutFromCart()
cy.shopFe_selectPaymentMethod("payWithCashMethod")
cy.shopFe_fillCheckoutFormAsGuest(guest)
cy.shopFe_checkoutPageGetOrderData()
cy.shopFe_submitCheckoutForm()
cy.shopFe_completePaymentWithCreditCard()
});
These 2 cases can be done in command with several variables, but in time those variables tend to increase tenfold. Thus we separate them into smaller repeatable parts that can be combined in different ways.

Typescript migration: Javascript "namespace" with multiple objects

I'm migrating some old js to ts. The file is of form (function implementations omitted for clarity):
// component.js
const Component = {}; // 'namespace' for components
Component.Base = function() {}
Component.A = function() {} // extends Base
Component.A.prototype.doStuffA = function() {}
Component.B = function() {} // extends Base
Component.B.prototype.doStuffB = function() {}
Component.C = function() {} // extends Base
// ... 100 other components, 2000 lines of code
export default Component;
In js, to use the file, I can do:
import Component from './component';
// 1. Instantiate one component
const compA = new Component.A();
// 2. or create multiple components
const threeComps = ['A', 'B', 'C'].map(name => new Component[name]() );
But in ts, I cannot even instantiate one component:
import Component from './component';
const compA: Component.A = new Component.A();
// tsc Cannot find namespace 'Component'
Question: What is the (quick) way to convert component.js into valid typescript, preferably keeping as many type-checks available as possible such
that
const compA: Component.A = new Component.B()
will be flagged as an error by the compiler.
I tried appending the following to the end of file:
namespace Component {
interface A {};
interface B {};
interface C {};
}
This seems to compile into correct javascript, but I would have to add all properties into interfaces. Seems tedious and violation of DRY-principle.
If you are going to migrate to TypeScript, you could immediately take advantage of the class syntax in your component.ts file:
export class Base {
}
export class A {
doStuffA() {}
}
export class B {
doStuffB() {}
}
export class C extends Base {
}
You can consume it using an import alias:
import * as Component from './component';
const a = new Component.A();
Or you can be selective with what you import:
import { A } from './component';
const a = new A();
Export Default / Modules Mixed With Namespaces
On the whole, the experts are saying that export default is a bad thing and that you shouldn't mix modules and namespaces.
You can do it, if you feel you must. here is the example with a namespace / default:
namespace Component {
export class Base {
}
export class A {
doStuffA() {}
}
export class B {
doStuffB() {}
}
export class C extends Base {
}
}
export default Component;
And the use:
import Component from './component';
const a = new Component.A();

Node.js v6.2.0 class extends is not a function error?

So I'm trying to extend a class in node js and the compiler keeps returning the following error:
TypeError: Class extends value #<Object> is not a function or null
I checked that I was exporting the class correctly and I am, any ideas? I'll post my code below:
/handler/venue.js:
var VenueViews = require('../views/venue'); // If I remove this the error will dissapear (as expected)
class Venue {
constructor(data) {
this.setDataHere = data;
}
main () {
var View = new VenueViews(); // This doesn't run
}
}
module.exports = Venue;
/views/venue.js:
var Venue = require('../handlers/venue');
console.log (Venue) // This returns {} ???
class VenueViews extends Venue {
constructor() {
super();
}
}
module.exports = VenueViews;
I know that node supports these es6 features, so I'm unsure why they aren't working?
Edit:
I'm not sure if this is suppose to happen but, when I log my Venue require it returns an empty object {}.
console.log (Venue) // This returns {} ???
So it turns out I had a circular reference in my code, where I was importing the class that was extending, into the class that itself was extending (tongue twister :P).
The obvious fix was to simply remove the extends reference and find another way of doing what I was trying to achieve. In my case it was passing the Venue class properties down into the VenueViews constructor.
E.g var x = VenueViews(this)
In my instance, it was the same issue as #James111 was experiencing (circular import) due to a factory pattern I was trying to set up in Typescript. My fix was to do move the code into files, similar to the following:
// ./src/interface.ts
import { ConcreteClass } from './concrete';
export interface BaseInterface {
someFunction(): any;
}
export class Factory {
static build(): BaseInterface {
return new ConcreteClass();
}
}
// ./src/base.ts
import { BaseInterface } from './interface';
class BaseClass implements BaseInterface {
someFunction(): any {
return true;
}
}
// ./src/concrete.ts
import { BaseClass } from './base';
export class ConcreteClass extends BaseClass {
someFunction(): any {
return false;
}
}
I had faced similar issue, after checking all the workaround finally issue got resolved by deleting the node_modules folder and run npm i.

Inheritance with Aurelia

Hi I am using Aurelia with Visual studio 2015.
I am creating a sports data related site.
Here I am having a base class which fetches basic details regarding the sports event. I inherit this base class from another class and fetch more information based on the base class property value.
Here is my base class --
import {inject} from 'aurelia-framework';
import {SportsEventService} from '../services/data-services/sports-event-service.js';
#inject(SportsEventService)
export class BaseClass{
sportsEvent;
constructor(sportsEventService){
this.SportsEventService = sportsEventService;
}
getSportsEventDetail(sportsEventName){
return this.SportsEventService.getSportsEventDetail(sportsEventName)
.then(response => {
this.sportsEvent = response;
return this.sportsEvent;
});
}
}
Here is my child class which inherits base class -
import {inject} from 'aurelia-framework';
import {SportsEventService} from '../services/data-services/sports-event-service.js';
import {SportsEvent} from './sports-event'
#inject(SportsEventService)
export class ChildClass extends BaseClass{
constructor(sportsEventService){
super(sportsEventService);
}
getUpcomingFixture(){
return (this.UpcomingFixtureService.getUpcomingFixture(this.sportsEvent.leagueId)
.then(response => {
return response;
}));
}
}
Here, when I am using baseClass property, "this.sportsEvent" it is giving me "Undefine".
Can anybody tell me, how can I make this work?
this.sportsEvent will be undefined until it's assigned at the this.sportsEvent = response; line.
You won't be able to call the ChildClass.getUpcomingFixture method until the base class's getSportsEventDetail method has finished because ChildClass.getUpcomingFixture depends on this.sportsEvent.
Try executing the code like this:
childClass.getSportsEventDetail(sportsEventName)
.then(() => childClass.getUpcomingFixture());

Categories