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.

Odd Behavior When Using useEffect Hook to Update Editor State

See original GitHub issue

Do you want to request a feature or report a bug?

Report a suspected bug with Slate, or identify an incompatibility with React hooks.

What’s the current behavior?

The editor / page crashes when pressing enter at the end of the content.

2019-12-11_16-19-29

Workflow:

In production, the text editor is controlled by a React component which first feeds the editor a default value.

import { initialValueHTML } from "./editor/data/initial-value-html";

this.state = {
  isLoading: false,
  value: initialValueHTML,
}
...
<RichTextEditor
  updateValue={this.updateValue}
  value={this.state.value}
 />

When the component has downloaded a new value from the database (think something saved from a prior entry), it then sends this along to the text editor to update.

The text editor looks for the updated props.value and uses the useEffect hook to update the state value:

const doc = new DOMParser().parseFromString(props.value, 'text/html');
const [value, setValue] = useState(deserialize(doc.body));
const [selection, setSelection] = useState(null);
useEffect(() => {setValue(deserialize(doc.body))}, [props.value]);
const editor = useMemo(() => withLinks(withRichText(withHistory(withReact(createEditor())))), []);

return(
	    <Slate
	      editor={editor}
	      value={value}
	      selection={selection}
	      onChange={(value, selection) => {
	        setValue(value)
	        setSelection(selection)
	        props.updateValue(serialize(editor));
	      }}
	    >
...

How to reproduce:

To reproduce the issue, and see the code behind it, navigate to:

http://studioscale-text-editor-test.s3-website-us-east-1.amazonaws.com/

You can type in the editor and format the text. Place the cursor at the end of the initial sample string, and press enter. This should cause the editor to crash.

Open up dev tools, and the root ‘break’ in the stack trace should be something akin to:

Error: Cannot get the start point in the node at path [1] because it has no start text node.

Troubleshooting:

I have noticed that this hook seems to be the culprit, and causes different behavior:

useEffect(() => {setValue(deserialize(doc.body))}, [props.value]);

When ‘looking for’ props.value to change, the editor crashes when you press enter at the end of

the content. When left out:

useEffect(() => {setValue(deserialize(doc.body))}, []);

This editor doesn’t crash, but the editor never receives the update past the original dummy value. The idea here was to make sure the editor has something to begin with, then update it when the database pull responds - but updating the state here causes it to crash.

Environment Info:

Slate: 0.53.0 Browser: Chrome OS: Linux

What’s the expected behavior?

The editor shouldn’t crash.

I see that one of the Built in Constraints is:

All Element nodes must contain at least one Text descendant. If an element node does not contain any children, an empty text node will be added as its only child. This constraint exists to ensure that the selection’s anchor and focus points (which rely on referencing text nodes) can always be placed inside any node. With this, empty elements (or void elements) wouldn’t be selectable.

Though, I’m not sure If I’m the one breaking this constraint, or if this specific use case is odd and just hasn’t been accounted for.

Disclaimer

One of the reasons I picked up React as a front end environment was because I could write everything in classes ( I come from a C++ background ). I’ll admit the React hooks are new grounds for me, and a paradigm shift to what I’m used to (classes). Hopefully, this is just a case of my ignorance in using them, and not something wrong with Slate.

Props

Mad props to all the devs of Slate. 53 is beautiful and so much easier to understand than prior versions.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

3reactions
gregphillips03commented, Dec 12, 2019

I did some digging into this, but wasn’t able to find anything in the core that I could override or change, with lasting effect.

Specifically looking here, at line 524 - 528.

      if (!Text.isText(node)) {
        throw new Error(
          `Cannot get the ${edge} point in the node at path [${at}] because it has no ${edge} text node.`
        )
      }

Troubleshooting:

I updated the sandbox site to mimic a database pull, then send the prop down to the editor. Just a setTimeout() that sends over a different html string.

I can see what @ianstormtaylor is saying in regards to not updating the selection. I.e., place the cursor in the word ‘bold’ before the fake pull is complete, and you’ll see the location is in the wrong spot, and still focused at [0, 1].

newdbpull

So new prop comes in, selection remains the same. If it’s in an existing part of the document that has a node structure, then it’s no big deal.

Again, go to the end of the last paragraph, press enter, it crashes as it doesn’t know where to place the selection, since a text node doesn’t exist (I think).

Newer Behavior

I did notice it’s not limited to just the end of the content. If you press enter in between to nodes it also causes the editor to crash. At first, I thought it was just at the end of the content - so advanced forwards would leave you in no-where-null-land, but it also happens within the content itself.

Thoughts:

Updating the selection’s state outside of the editor seems problematic with useEffect, if it’s updated every time props.value (the html string) changes. Feels like it would make the behavior unpredictable, given that the editor should generally handle where a selection lives. That feels a little separation of concerns-y, but I can’t think of a good way to update the selection on a props change, outside the editor.

I did try passing the selection back and forth (the same as with the actual value of the editor, using props.updateValue , but it didn’t work.

So not to point the finger back at Slate, but I feel the PR would be the best route - unless someone can brainstorm a good way to update selection outside the editor. Unfortunately, both are beyond what I can contribute.

Gotcha

If the core logic changes, and then no longer crashes, but deselects the editor - how will you be able to add a new line? As in your’e done with the current paragraph, press enter, new line. This question keeps bugging me, and I can’t see a solution. Feels like you’d press enter, the console would get a message, the document would deselect, and then you’d be stuck in an endless loop?

Reading through some of the older changes, it looks as if this was handled where the editor would insert a new text node, if it ran into this - but that was frowned on by some, as it was deemed business-logic-y?

Again, changing that is beyond me - merely thinking aloud. I don’t like to bring a problem and not try a solution, but I think this is beyond what I can help with.

2reactions
ianstormtaylorcommented, Dec 11, 2019

I believe this is because you’re updating the value but not the selection, so the selection then points to a non-existent part of the document.

I’d be down for a PR that changes the core logic to instead log a warning along the lines of “the selection was invalid”, and instead deselects the editor.

Read more comments on GitHub >

github_iconTop Results From Across the Web

React useEffect weird behavior - Stack Overflow
In the edit parent component i use useEffect to fetch data from an api and set the initial state from the server.
Read more >
Understanding common frustrations with React Hooks
React Hooks can be frustrating despite their popularity and widespread use. Learn about some of the drawbacks to using React Hooks.
Read more >
React.useEffect Hook – Common Problems and How to Fix ...
The first and probably most obvious option is to remove the dependency from the useEffect dependency array, ignore the ESLint rule, and move...
Read more >
Hooks FAQ - React
Both useState and useReducer Hooks bail out of updates if the next value is the same as the previous one. Mutating state in...
Read more >
The tricky behavior of useEffect hook in React 18 - Medium
useEffect (() => { console. · You will see this log twice for dev mode, once after state change - double effect call....
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