Search code examples
javascriptclass-constructors

Javascript class constructor not setting class variables as expected


I'm trying to set API endpoints by environment in a javascript helper class I'm using to pull data into my react app. It looks like this:

import axios from 'axios';

class QueryHelper {
    endpoints;
    SupportEndpoint;
    MemberEndpoint;

    constructor() {
        debugger
        // get the endpoints set
        fetch('/environment')
            .then(response => response.json())
            .then(data => this.endpoints = data) // set the endpoints var with the json payload with all the endpoint and env data
            .then(() => {
                this.SupportEndpoint = this.endpoints.supportBaseUrl;
                this.MemberEndpoint = this.endpoints.memberBaseUrl;
            });
    }

    
    async fetchData(resource) {
        const resp = await axios.get(this.SupportEndpoint + '/' + resource);
        return resp.data;
    }
}

export default QueryHelper;

It would get used like this:

let helper = new QueryHelper();
helper.fetchData('MemberProfile')
    .then(response => response.json())
    .then(//do some other stuff);

When I hit the breakpoint, I can step through the constructor and the various endpoints seem to be getting set as expected. But when the fetchData method is called, the SupportEndpoint (and any other endpoint) is undefined and the ajax call fails with a 404.


Solution

  • The constructor is asynchronously setting those props. It’s fine to wait till fetch("/environment") resolve then call helper.fetchData(), but if you call it too soon after instantiation, then it fails.

    You need some mechanism to ensure the helper isRready.

    function defer() {
        let resolve, reject;
        const promise = new Promise((r, j) => {
            resolve = r;
            reject = j;
        });
        return { resolve, reject, promise };
    }
    
    class QueryHelper {
        endpoints;
        SupportEndpoint;
        MemberEndpoint;
    
        _isReady;
        _d;
        get isReady() {
            if (this._isReady) return Promise.resolve(true);
            if (!this._d) {
                this._d = defer();
            }
            return this._d.promise;
        }
    
        constructor() {
            fetch('/environment')
                .then(response => response.json())
                .then(data => this.endpoints = data) // set the endpoints var with the json payload with all the endpoint and env data
                .then(() => {
                    this.SupportEndpoint = this.endpoints.supportBaseUrl;
                    this.MemberEndpoint = this.endpoints.memberBaseUrl;
                    this._isReady = true;
                    if (this._d && this._d.resolve) this._d.resolve(true);
                });
        }
    
        
        async fetchData(resource) {
            await this.isReady;
            const resp = await axios.get(this.SupportEndpoint + '/' + resource);
            return resp.data;
        }
    }