I’m sporadically seeing “Missing target element” JS errors fired during connect()
, but they also come with an error log regarding circular JSON structure, in controllers that do not parse JSON, which seems very strange to me.
Some salient points for background:
- I’m using Turbolinks, and I bundle Stimulus via Webpack.
- The controllers exist once per page.
- The targets that are mentioned in the error reports are not conditionally rendered, the entire component is either there or not.
- The controllers that are erroring don’t handle JSON through any code paths that
connect()
invokes. - I’ve only seen this in Chrome on Windows.
- It is very very infrequent, less than a percentage of my requests. I have not been able to repro it.
- These controllers do some work in
connect()
. One accesses media devices and rebuilds someselect
elements. Another tests some data attributes and then does some class manipulation on its targets.
Here are a couple example logs of the issue:
10:34:57.960 0.000s Log document not ready yet, trying again in 500 milliseconds...
10:34:58.802 0.842s Log Error connecting controller {} TypeError: Converting circular structure to JSON
10:34:58.803 0.843s Error Missing target element "phone.bridged"
10:34:58.810 0.850s Log Error connecting controller {} TypeError: Converting circular structure to JSON
10:34:58.810 0.850s Error Missing target element "phone.dial"
10:35:05.016 7.056s Error #1192 Missing target element "preferences.listen" (occurrence 44159716805)
14:26:42.727 0.000s Log Error connecting controller {} TypeError: Converting circular structure to JSON
14:26:42.728 0.001s Error Missing target element "phone.bridged"
14:26:42.778 0.051s Log WebSocket opened successfully.
14:26:42.779 0.052s Log [PStream] Setting token and publishing listen
14:26:43.991 1.264s Error #1243 Missing target element "preferences.listen" (occurrence 44390982945)
My current thought is perhaps I shouldn’t do a lot of work in connect()
. With Stimulus I’m no longer directly hooked into the Turbolinks load
event. With previous JS component systems I’ve mounted components in response to turbolinks:load
, and done teardown via turbolinks:before-cache
. I’ve already added a teardown()
method to my Stimulus base controller that I am now executing in response to turbolinks:before-cache
to do DOM cleanup, perhaps I should add a similar lifecycle method for doing heavy lifting after the component is connected? This seems a little dramatic for a small quantity of errors though.