@Wender Xavier
I’ve encountered some of this weirdness as well. I think I have a fix for you.
A couple of ideas here:
I roughly think this is what’s going on…
As you have it coded, it would appear that every time React re-renders/mounts your myComponent
you’ll be adding a new listener. Note that after the Miro board is fully loaded the miro.onReady()
method will immediately call the callback function - therefore, that callback you have, with the miro.addListenter
in there, will get called each time the MyComponent
is re-rendered. And, as you’re re-rendering the component every time the state changes you’ll add a new listener each time you update count
-- so each listener is setting up a another callback function which updates state and adds another listener… so -- I think this is essentially what’s at the root of the bizarre ‘doubling’.
Solution:
Add the listener using the useEffect
hook (quick tutorial here) with a blank array as the argument, and this will run that code just once when the component is first mounted.
I got your code working with the structure below. At least I think its what you’re going for.
import React, { useState, useEffect } from "react";
const miro = window.miro;
const MyComponent = () => {
const ncounter, updateCounter] = useState(0);
useEffect(() => {
setListener();
}, /]);
function setListener() {
miro.addListener("SELECTION_UPDATED", () => {
console.log("selection updated triggered");
updateCounter((counter) => counter + 1);
});
}
return <div>{counter}</div>;
};
export default MyComponent;
Video of it working here:
https://www.loom.com/share/e7144a0f60b74bdeab7498cd76a94dc3
Note also, there is miro.removeListener() to remove a listener … but that seems a wrong fit for what you’re trying to do.
miro.onReady()
I advise using miro.onReady()
in index.js
to initiate the entire react app only once Miro is ready… what this ensures is that prior to loading your app, that the miro
global variable is established and the methods that are to be used will be available when any of your code within your app calls upon those methods.
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "./index.css";
window.miro.onReady(() => {
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
});
miro = window.miro
I declare miro = window.miro
in a <script>miro = window.miro </script>
in the bottom of the app’s index.html
And, I also declare miro = window.miro
at the top, after the imports, of each component that has miro methods i.e. miro.____
. I’m not sure what’s “Perfect” or “Correct” - but I find that that technique works… so I go with it.
create-react-app dev server weirdness:
Additionally … I’ve found while developing react-based Miro plugins using create-react-app and its built-in development server, edits made to your code will trigger a refresh of the app in the plugin iframe but the state variables in your react app will remain and all of the prior session listeners that your app registered with Miro will remain, and you’re app, re-rendering from the top level component down, will invariably re-register listeners again with miro - causing there to be extra listeners now running in that session.
To fix this, one must “X” close the plugin, and reload it from the toolbar, or refresh the Miro board (refresh the browser page). You won’t need to X-out each time you make any change, UI changes won’t be an issue… but do stay mindful as you work with functionality related to listeners.
Hopefully this helps.
Max
Thank you @Max Harper.
After your detailed explanation I could see what I was doing wrong.
Managed to get it working now!
Very helpful! Thank you very much!