question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Between v10.3.4 and v10.4.8, JSX.Element became incompatible with immutable.js

See original GitHub issue

Summary

As of v10.4.8, storing a Preact JSX.Element in one of the immutable.js container classes will result in a crash when you extract it with .toJS(). This did not occur with v10.1.0.

I believe immutable.js is mistaking the JSX.Element for an immutable.js class and attempting to “unpack” it infinitely.

Repro

Here (branch preact-immutable-crash) is a small typescript+webpack+preact app. Test it with

npm install && npx webpack && (cd site && http-server -c-1)

(Assuming http-server previously installed with npm install -g http-server.)

If you build and load commit eee4, it works just fine. If you build and load commit 2c20, it crashes. The only difference between the two commits is instead of Preact v10.1.0, it uses 10.4.8.

The crash is an uncaught “RangeError: Maximum call stack size exceeded” exception somewhere deep in the guts of immutable.js.

Where the problem occurs

I don’t think webpack or typescript are relevant to the problem; the code that triggers the problem seems to be merely

let demoSet = OrderedSet<string>().add("one").add("two")

const divArray = demoSet.map(
    s => <div className="Id">{s}</div>
).toJS()

render(<div>Hello<br />{divArray}</div>, domNodeHere)

The problem occurs on the “.toJS” call. “map” is a lazy version of JS map(); my understanding(?) is the mapped function shouldn’t be called until you try to extract the data. Saying .toJS().map( would avoid the crash, but I thought(?) I was avoiding an allocation by doing it in the other order.

Variants

While writing this bug I realized I could make the crash happen with an even simpler repro:

const simplerTest = List<h.JSX.Element>().push(<div>One</div>)
const divArray = simplerTest.toJS() // Crash on this line

I have also tested, and seen the same crash, with OrderedSet instead of List (though in retrospect it doesn’t make a lot of sense to keep a JSX.Element in a set).

If instead of saying .toJS() I say .toArray(), the crash does not occur. The difference is that toList is a “shallow” operation and toArray is recursive. If you have an immutable List of immutable Lists of strings, toArray will produce a JS array of immutable Lists of strings and toJS will produce a JS array of JS arrays of strings. In other words it is safe to store a JSX.Element in a immutable.js structure and extract it, the only unsafe thing is an operation where immutable.js could potentially interpret a JSX.Element as a immutable.js container.

Impact

Immutable.js is obviously well fit to React/Preact; in my experience, contexts are virtually unusable without immutable data structures. So any incompatibility between Preact and Immutable.js is pretty bad.

The problem is less bad than it could be because it only occurs with .toJS(). (Checking Google I find people recommending always using .toArray() over .toJS() anyway, for performance reasons.)

I think the problem is still pretty bad because (1) storing a List of Lists and then unpacking it with .toJS is a reasonable thing to do (2) the error message you get is extremely misleading, and Chrome prints out a completely broken stack when it occurs unless you specifically run in the debugger. I encountered this error on a project and it took me a long time to figure out that .toJS() was the cause or even that the reason the error had occurred was because I upgraded Preact.

Template questions

“Expected behavior”

I should be able to store and extract JSX.Elements from immutable.js the same way I could a string using any method, including .toJS().

“For npm, copy & paste the output of this terminal command: npm ls --depth 0 | grep preact”

├── preact@10.4.8

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
mcclurecommented, Sep 12, 2020

Yeah, based on your description it sounds like this is more appropriate to fix in immutable than in preact. Thanks for the help

0reactions
marvinhagemeistercommented, Sep 12, 2020

Good point, you should forward that to the immutable.js maintainers 👍 It would set a bad example if we start adding workarounds in Preact for issues in other libraries. It certainly isn’t doable even if we wanted to, given our own size restrictions.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Immutable.js
Convert from raw JavaScript objects and arrays. Designed to inter-operate with your existing JavaScript, Immutable.js accepts plain JavaScript Arrays and ...
Read more >
Changelog
Vue.js projects with a custom spec pattern configuration can now use the "Create Spec from Component" capability introduced in v10.5.0. Addresses #23071.
Read more >
pgAdmin 4 Documentation - Index of - PostgreSQL
from pgadmin.utils import env, IS_WIN, fs_short_path ... only supported when pgAdmin is using libpq v10.0 or later to connect to the server.
Read more >
Keysight KCOS 9.17 Third Party & Open-Source License ...
@babel/helper-builder-react-jsx 7.9.0 : MIT License ... immutable-js 3.7.6 : Facebook BSD License with Patent Provision.
Read more >
third party component notices
jake v10.8.2 java-diff-utils 1.3.0. JavaWriter 2.1.1. JavaWriter 2.5.0. Javax Inject from the JSR-330 Expert Group 1 java-xmlbuilder 0.4.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found