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.

Explain a quirk with reference to node objects

See original GitHub issue

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

feature

What’s the current behavior?

Slate treats the node as a unique object reference. This is evident by the use of WeakMap<Node, *>, where the node object reference is used as the identifier.

But nothing prevents me from inserting the same node multiple times. Here’s how easy it is to make the mistake. A diff of editable-voids.js example:

diff --git a/site/examples/editable-voids.js b/site/examples/editable-voids.js
index c2187cc9..8a871a8b 100644
--- a/site/examples/editable-voids.js
+++ b/site/examples/editable-voids.js
@@ -7,6 +7,8 @@ import { css } from 'emotion'
 import RichTextEditor from './richtext'
 import { Button, Icon, Toolbar } from '../components'
 
+const VOID_NODE = { type: 'editable-void', children: [{ text: '' }] }
+
 const EditableVoidsExample = () => {
   const [value, setValue] = useState(initialValue)
   const editor = useMemo(
@@ -39,9 +41,7 @@ const withEditableVoids = editor => {
 }
 
 const insertEditableVoid = editor => {
-  const text = { text: '' }
-  const voidNode = { type: 'editable-void', children: [text] }
-  Transforms.insertNodes(editor, voidNode)
+  Transforms.insertNodes(editor, VOID_NODE)
 }
 
 const Element = props => {

The voidNode is the same every time, so it’s not by any stretch of the imagination that someone might see and be “Oh, I can extract that as a constant instead.”, and produce code similar to the diff above. I’m saying that because it’s almost exactly what I did in our codebase.

What follows is after inserting multiple of these nodes, you’ll see “Encountered two children with the same key” warning. This is not that terrible, but the same problems ensue with every bit of logic which relies on the node object reference being unique.

Slate: 0.58.4 (but probably applies to any version after 0.50’s migration to native objects) Browser: Chrome / Safari / Firefox / Edge OS: Mac / Windows / Linux / iOS / Android

What’s the expected behavior?

It would be nice to have some kind of warning when inserting a duplicate node reference.

But for starters, it would be ok to explain this quirk in the docs.

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:3
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
BrentFaresecommented, Aug 1, 2020

There are areas of Slate where the node is copied in the insertion vs. passed in directly. Also, Slate uses Immer under the hood when applying ops so I’m not sure if what you are doing is actually keeping the object reference in editor.children in multiple places. Have you actually validated that it’s the same object (e.g., by using === on the same node extracted from editor.children)? I would be interested to know if Immer does something under the hood that makes it not the same object reference…

0reactions
markogresakcommented, Aug 3, 2020

@BrentFarese I have tested it, and yes, it’s the exact same node.

Steps:

  1. Apply the following diff
diff --git a/site/examples/editable-voids.js b/site/examples/editable-voids.js
index c2187cc9..9ca72ab9 100644
--- a/site/examples/editable-voids.js
+++ b/site/examples/editable-voids.js
@@ -7,6 +7,8 @@ import { css } from 'emotion'
 import RichTextEditor from './richtext'
 import { Button, Icon, Toolbar } from '../components'
 
+const VOID_NODE = { type: 'editable-void', children: [{ text: '' }] }
+
 const EditableVoidsExample = () => {
   const [value, setValue] = useState(initialValue)
   const editor = useMemo(
@@ -39,9 +41,10 @@ const withEditableVoids = editor => {
 }
 
 const insertEditableVoid = editor => {
-  const text = { text: '' }
-  const voidNode = { type: 'editable-void', children: [text] }
-  Transforms.insertNodes(editor, voidNode)
+  Transforms.insertNodes(editor, VOID_NODE)
+  console.log(
+    editor.children.map((node, i, children) => children.indexOf(node))
+  )
 }
 
 const Element = props => {
  1. Go to /examples/editable-voids and press the add button twice, to add 2 more editable voids nodes (calls insertEditableVoid twice).

  2. The second time, the console.log outputs [0, 1, 2, 3, 3]. This confirms it’s the exact same node object.


Slate uses Immer under the hood when applying ops

https://github.com/ianstormtaylor/slate/blob/1cf63f0a19a45c3b35ea3f8682e57a28cacfb4d1/packages/slate/src/interfaces/editor.ts#L1255-L1258

There is no sign of immer when working with the node to insert. The code takes op.node and throws it in the editor.children, without creating a copy of the object.

Read more comments on GitHub >

github_iconTop Results From Across the Web

A Javascript quirk that will catch you out - freeCodeCamp
This is because objects are copied by reference. copyObject is just a reference to the underlying data. So when we change the name...
Read more >
Seven JavaScript Quirks I Wish I'd Known About - Telerik
You might already know that you can take the marty object's timeTravel method and create a new reference to it from another object....
Read more >
12 JavaScript quirks - 2ality
ECMAScript 5 will be used and a basic knowledge of JavaScript is required, but much will be explained. I will post one quirk...
Read more >
SPICE Quirks | Using the Spice Circuit Simulation Program
In both these cases, the voltage polarity is negative at node 0 with reference to the other node (in other words, both nodes...
Read more >
A quirk with object dependencies under EBR - svenweller
The basics Oracle tracks dependencies between objects, ... I think they come from references to datatypes that are defined there.
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