When you pass an observable HTMLElement to toJS, a RangeError occurs:
mobx.js:427 Uncaught (in promise) RangeError: Maximum call stack size exceeded(…)
Working on a fix for this, but having a hard time writing tests, since there is no DOM in the test runner.
@maxdeviant, what do you mean by “observable HTMLElement”? Please, don't say that you are doing something like extendObservable(myDiv, {}) :-)
Working on a fix for this, but having a hard time writing tests, since there is no DOM in the test runner.
You can run it in chrome like this: npm run test-browser-chrome, but note that mobx should not be tight to DOM.
Strange, that this error occurs, it have to be prevented by __alreadySeen check.
@andykog Well, my original case was something like this:
class MyStore {
@observable
public images = asMap<HTMLImageElement[]>({
123: new Image()
});
}
// somewhere else in my code
console.log(toJS(myStoreInstance.images));
// throws the RangeError
What I am really interested in is when entries are added to/removed from the map, not necessarily observing the element itself.
note that mobx should not be tight to DOM.
I noticed that when I tried a simple instanceof HTMLElement check and it said HTMLElement was undefined :sweat_smile:
It appears that there are ways to check for an HTML element without instanceof, like looking for certain properties, etc.
Well instanceof check can be made safely like this:
var checkIfDomNode = (typeof document === 'object' && document && document.createElement && typeof HTMLElement === 'function' && typeof Node === 'function')
? s => s instanceof Node
: () => false;
Ah, good point. I had it done like this:
function isHTMLElement(source) {
try {
return source instanceof HTMLElement;
} catch (err) {
return (
source !== null &&
typeof source === "object" &&
source.nodeType === 1 &&
typeof source.style === "object" &&
typeof source.ownerDocument === "object"
);
}
}
Also, is there a way to add a test that only runs when executed in a browser? It doesn't appear that process.env.BROWSER (or something equivalent) is being set anywhere when running the test suite.
My approach is a bit more performant :-)
Also, is there a way to add a test that only runs when executed in a browser? It doesn't appear that NODE_ENV is being set anywhere when running the test suite.
Nope, but you can detect DOM the same way:
t.test('...', function(t) {
if (typeof document === 'object') {
toJS(document.createElement('img'));
}
t.end();
}
I'm not entirely sure about this PR;
alreadySeen mechanism should prevent this behavior, maybe there is a bug in theretoJS is actually desired. Probably it should only process plain objects / arrays and collections known to MobX, but not other objects that have a prototype? (Or maybe just their observable properties and nothing more)@mweststrate, this bothers me in current toJS behaviour as well, but to change it we have to at least somehow mark classes with properties decorated with @observable
@andykog they return true in isObservableObject. From there on we could iterate the observable, non computed properties as stored in the $mobx administration object
@mweststrate You are correct in that my PR is not really generic, since it grew out of an issue that we were encountering in our codebase.
So if there is a better solution that can be achieved that fixes the issue, I am all for that.
Closing this issue as it has been solved in 2.7.0. Feel free to re-open if this isn't the case.