2

I am getting this error Error: A 'pure' computed must not be called recursively after I add a few lines of code to my js file and I can't figure out why. Here is the beginning of the file:

import knockout from 'knockout';
import BaseViewModel from '../checkout/base';
import Is from '../is';
import jQuery from 'jquery';

const AddressViewModel = ((ko, is, $) => {

    const CountryCode = {
        CANADA: 'CA',
        USA: 'US'
    };

    const CITY_ENTER_OTHER = 'Enter Other City';

    class AddressViewModel extends BaseViewModel {

        constructor() {
            super();

            this.addressId = ko.observable();
            this.locationDescription = ko.observable();
            this.addressLine1 = ko.observable();
            this.addressLine2 = ko.observable();
            this.postalCode = ko.observable();
            this.cityEntered = ko.observable();
            this.citySelected = ko.observable();
            this.cityOptions = ko.observableArray();
            this.countryCode = ko.observable();
            this.state = ko.observable();
            this.province = ko.observable();
            this.addressTypeCode = ko.observable();
            this.alreadyChecked = ko.observable(false);
            this.stateCheck = ko.observable(false);
            this.initialValueUsed = ko.observable(false);

            this._initComputedValues();
            this._initSubscribers();
        }

        _initComputedValues() {
            // this next code is what I added that is causing the error
            this.addressLine1 = ko.pureComputed({
                read: this._readAddressLine1,
                write: this._writeAddressLine1,
                owner: this
            });
            this.postalCode = ko.pureComputed({
                read: this._readPostalCode,
                write: this._writePostalCode,
                owner: this
            });

Those last two blocks starting with this. are what I have recently added in. I also added the read/write definitions down later in the file. Outside of the _initComputedValues function I have added these as well:

_readAddressLine1() {
        let address = this.addressLine1();
        let returnValue = this.replaceSpecialCharacters(address);
        return returnValue;
    }

    _writeAddressLine1(address) {
        this.addressLine1(address);
    }

    _readPostalCode() {
        let postalCode = this.postalCode();
        let returnValue = this.replaceSpecialCharacters(postalCode);
        return returnValue;
    }

    _writePostalCode(postalCode) {
        this.postalCode(postalCode);
    }

Now after adding that code in is when I started getting that error.

And finally, my replaceSpecialCharacters function is coming from another file called convert-special-characters.js:

import BaseViewModel from '../checkout/base';

const CharacterViewModel = (() => {

    class CharacterViewModel extends BaseViewModel {

        constructor() {
            super();

            this._initComputedValues();

        }

        _initComputedValues() {
            this.createConversionMap = this._createConversionMap.bind(this);
            this.replaceSpecialCharacters = this._replaceSpecialCharacters.bind(this);
        }

        _createConversionMap() { // from https://stackoverflow.com/a/49139933/571723

            let map = {};

            // Open-quotes: http://www.fileformat.info/info/unicode/category/Pi/list.htm
            map[0x2018] = '\'';
            map[0x201B] = '\'';
            map[0x201C] = '"';
            map[0x201F] = '"';

            // Close-quotes: http://www.fileformat.info/info/unicode/category/Pf/list.htm
            map[0x2019] = '\'';
            map[0x201D] = '\"';

            // Primes: http://www.fileformat.info/info/unicode/category/Po/list.htm
            map[0x2032] = '\'';
            map[0x2033] = '"';
            map[0x2035] = '\'';
            map[0x2036] = '"';

            map[0x2014] = '-'; // iOS 11 also replaces dashes with em-dash
            map[0x2013] = '-'; // and "--" with en-dash

            return map;
        }

        _replaceSpecialCharacters(value) {
            let conversionMap = this.createConversionMap;
            let returnValue = '';

            for (let i = 0; i < value.length; i++) {
                let replacement = conversionMap[value.charAt(i)];
                if (replacement) {
                    returnValue = value.replace(value.charAt(i), replacement);
                }
            }
            return returnValue;
        }


    }

    return CharacterViewModel;
})();

export default CharacterViewModel;
dmikester1
  • 1,374
  • 11
  • 55
  • 113

1 Answers1

3

You've declared addressLine1 as an observable at the top, and then declared it again as a computed later. I'm guessing these were supposed to be two different properties? The computed version is trying to return the value of the observable one and that's the recursion that's happening. You can't have a computed use itself as the backing field.

this.addressLine1 = ko.observable();

...

this.addressLine1 = ko.pureComputed({
    read: this._readAddressLine1,
    write: this._writeAddressLine1,
    owner: this
});

...

_readAddressLine1() {
    let address = this.addressLine1();
    let returnValue = this.replaceSpecialCharacters(address);
    return returnValue;
}

A computed is just a function with an automatic trigger; it doesn't ever store values. Having it use its own value within its definition is asking it to solve for x where x is some function of x. You'd get a stack overflow if it didn't give you a helpful compile error instead.You should probably rename the observable to something like _addressLine1 so the code reads as:

this._addressLine1 = ko.observable();

this.addressLine1 = ko.pureComputed({
    read: this._readAddressLine1,
    write: this._writeAddressLine1,
    owner: this
});

...

_readAddressLine1() {
    let address = this._addressLine1();
    let returnValue = this.replaceSpecialCharacters(address);
    return returnValue;
}
_writeAddressLine1(address) {
    this._addressLine1(address);
}
Jason Spake
  • 4,293
  • 2
  • 14
  • 22