nbudin / react-blockly

A React component that embeds a Blockly visual programming editor.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

blocks are injected twice

randrei12 opened this issue · comments

During react renders the blocks' xml ends up injecting twice.

    const { loaded, project, updateProject } = useProject(); //here is stored the workspace (into data.blocks property)
    const blocklyRef = useRef(null);

    const { xml, workspace } = useBlocklyWorkspace({
        ref: blocklyRef,
        toolboxConfiguration: toolbox,
        initialXml: '',
        workspaceConfiguration: { zoom: { controls: true, wheel: true, startScale: 1, maxScale: 3, minScale: 0.3, scaleSpeed: 1.2 }, theme },
        onWorkspaceChange: (workspace) => {
            if (loaded) {
                console.log(xml);
            }
        }
    });

    useEffect(() => {
        if (loaded) {
            Blockly.serialization.workspaces.load(JSON.parse(project.data.blocks), Blockly.getMainWorkspace());
        }
    }, [loaded]);

    return (
        <div id="blockly" ref={blocklyRef}></div>
    );

As you can see from the following image, the xml ends up doubling each block
image

Thanks in advance!

Hi @randrei12! I'm not sure that this log actually indicates an issue. onWorkspaceChange can be called many times; there is no guarantee that it won't be called multiple times with the same data even for just one change. This is usually not a problem, because typically what you'd be doing with the data passed into that function is to pass it into a state setter, and that's fine because setting the state to the same thing it's already set to won't do anything.

I do see that in each invocation of console.log in your screenshot, there don't appear to be duplicated blocks - each block has a unique ID within each log line. It's just that the log lines themselves are coming multiple times, because the onWorkspaceChange function is called more often than you expect.

Does this make sense, or am I missing a different issue?

Hello, @nbudin and thanks for your help! The logs are invoked all at once, at start. As you can see, the xml has only one block at the beggining (as it should be) then it suddenly ends up having another block, which is a clone of the first one. I don't understand why it ends up cloning the first block. The following json is loaded by the workspace:

{
    "blocks": {
        "languageVersion": 0,
        "blocks": [
            {
                "type": "go_to",
                "id": "k3UocM0qeyD#DYnrCrh+",
                "x": 36,
                "y": 36,
                "inputs": {
                    "LOCATION": {
                        "shadow": {
                            "type": "text",
                            "id": "I_H@.8]?S=N$vyd#FDQX",
                            "fields": {
                                "TEXT": ""
                            }
                        }
                    }
                }
            }
        ]
    }
}

As you can see, there's only one block, not two.

Interesting. I think this can happen if Blockly.serialization.workspaces.load is invoked twice (in other words, it doesn't clear out the existing contents of the workspace). I'm not sure exactly why that would be happening, but if loaded can change multiple times, the useEffect could run multiple times.

Without seeing the contents of the useProject hook, it's hard to say when and how loaded might change.

I added the following log, so I can see how many times it ran:

    useEffect(() => {
        if (loaded) {
            Blockly.serialization.workspaces.load(JSON.parse(project.data.blocks), Blockly.getMainWorkspace());
            console.log('it loaded');
            
        }
    }, [loaded]);

As you can see, it ran only one time:

image

Whoa, super weird!

Would you mind sending me a copy of your code so I can try it out on my machine? If you don't feel comfortable sharing it publicly, you can email it to me at natbudin@gmail.com.

Hello @nbudin! The problem is not with my project, as I was able to recreate the problem in a sandbox (Link). I strongly believe the problem is with workspace parse, which triggers multiple domToWorkspace function runs.

Hello @nbudin! The problem is not with my project, as I was able to recreate the problem in a sandbox (Link). I strongly believe the problem is with workspace parse, which triggers multiple domToWorkspace function runs.

Ok, thanks @randrei12! I'll be out of town on a trip until Tuesday, so I probably won't be able to debug further until then. If you come up with a workaround or a patch, please let me know and I'll try to get something out! Otherwise, I'll be able to look at this in depth on Tuesday.

Ok, I'll try to debug further and find the root cause. Thanks for your help!

I see @randrei12 closed the issue but, FWIW, I'm seeing a similar issue where the first block I drag into the workspace is duplicated. However, subsequent drag operations do not duplicate the block.

Here is are my workspace params. Of note, the OnWorkspaceChange function is completely empty and this still occurs:

function MyBlocklyEditor() {
      const [xml, setXml] = useState("");

      return (
        <BlocklyWorkspace
          className="width-100" // you can use whatever classes are appropriate for your app's CSS
          toolboxConfiguration={theToolbox} // this must be a JSON toolbox definition
    
          workspaceConfiguration={
            {
              grid: {
                spacing: 20,
                length: 3,
                colour: "#000000",
                snap: true,
              
              },
            }
          }

          // initialXml={ConfigFiles.INITIAL_XML}
          initialXml={xml}
          onXmlChange={setXml}
          onWorkspaceChange={workspaceDidChange}
        />
      )
    }

version in use
"blockly": "^9.0.0", "react": "^18.2.0", "react-blockly": "^7.1.0",

Visualization of this "double dragon" situation I'm seeing
https://user-images.githubusercontent.com/14134766/222878470-1802c886-c147-434a-972f-8fa86053feeb.mp4

Hello, @Chambana. Do you use Blockly.serialization.workspaces.load?