facebook / lexical

Lexical is an extensible text editor framework that provides excellent reliability, accessibility and performance.
https://lexical.dev
MIT License
19.31k stars 1.63k forks source link

Feature: infinite loop detection for updates #3694

Open DaniGuardiola opened 1 year ago

DaniGuardiola commented 1 year ago

I spent a few hours debugging an infinite loop issue today in our Lexical editor. Turns out we had pushed code like this:

editor.registerUpdateListener(() => {
  editor.update(() => {
    // actually just reading the state here
  })
})

This caused an infinite update loop under certain (obscure) circumstances. I'm unclear on why not always, but that's besides the point.

My proposal would be to introduce a check, similar to the one React does for renders, or to the one Lexical already does for node transforms, to error after an unreasonable amount of synchronous update cycles. Debugging infinite loops is a pain in the lower part of the back: your browser (and devtools) become unresponsive, and you have to start over every time to replicate the bug. It also prevents tools like Replay from successfully recording the bug, since they can't finish and upload the recording if the main thread is permanently blocked.

I'm down to contribute this change, btw. I would appreciate some directions to do so, just a few bullet points in the context of this task.

trueadm commented 1 year ago

It would be good to know if the update loop is synchronous each time. In which case we can use a counter like we already do for infinite loop detection on node transforms. Did you want to contribute this change? We'd be happy to help you.

DaniGuardiola commented 1 year ago

@trueadm yes, this happened synchronously (react 17 so no concurrency anyway), which is why the main JS thread ends up blocked forever.

Yeah will contribute the change. I just had to debug another prod-breaking bug due to this lol, so will get to it ASAP. At least this time I was able to fix in 5 minutes.

hugs7 commented 2 months ago

Looking for a way to update the state when something particular changes in the selection. How are you meant to do this without calling editor.update() within the editor.registerUpdateListener((event) =>{...}) callback?

Sergio16T commented 2 months ago

@hugs7 the recommended approach is to use node transforms. You can register a node transformation on the root node which references the editable content root node which will run on each update to the text content.

See documentation here: https://lexical.dev/docs/concepts/transforms https://lexical.dev/docs/concepts/nodes