Search code examples
javascriptreactjscreatejs

How do I tell React component to change its state when an event is triggered from third party lib?


I am using createjs to create an canvas, and there is a double click event in this canvas, now I want to tell a React component when the event is triggered, and set state to a variable that defined in the custom function. for example, I want to set the value to 'pose.position' that's in the custom function.

my react component:

const [value, setValue] = useState(null);
const handleEvent = event => {
   console.log(value);
};
useEffect(() => {
const ros = new ROSLIB.Ros({
   url
});
const viewer = new ROS2D.Viewer({
   divID,
   width,
   height
});
const nav = NAV2D.OccupancyGridClientNav({
   ros,
   rootObject: viewer.scene,
   viewer,
   serverName,
   continuous
});
setValue(nav.position);

const canvas = divEl.current.children[0];

canvas.addEventListener("dblclick", handleEvent, false);

return () => {
    canvas.removeEventListener("dblclick", handleEvnet);
};
}, []);

return <div id={divID} ref={divEl} />;

and the custom function:

this.rootObject.addEventListener("dblclick", function(event) {
  // convert to ROS coordinates
  const coords = stage.globalToRos(event.stageX, event.stageY);
  const pose = new ROSLIB.Pose({
     position: new ROSLIB.Vector3(coords)
  });
  // send the goal
  sendGoal(pose);
  that.position = pose.position;
  // that.mouseClick = true;
  console.log("clicked");
  });
  //I do not know how my React component can get this value, so I just return
  return this.position;

The problem is the value in component only set once,and the log function in handleEvent outputs null, the value will not be updated.How do I notify React that the value is changed? Should I create another useEffect(()=>{}, [value])?

OR

Can I dispatch an action in this.rootObject.addEventListener so I can notify react to re-render some components? Can I use redux in non-react function?


Solution

  • From what I see there are 2 options to get this working:
    1) Put the part that calculates position to the React component:

    const [value, setValue] = useState(null);
    const handleEvent = event => {
      // convert to ROS coordinates
      const coords = stage.globalToRos(event.stageX, event.stageY);
      const pose = new ROSLIB.Pose({
        position: new ROSLIB.Vector3(coords)
      });
      setValue(pose.position);
    };
    

    2) Introduce an extra event and listen for it in the component. So that your custom function will look something like this:

    this.rootObject.addEventListener("dblclick", function(event) {
      // convert to ROS coordinates
      const coords = stage.globalToRos(event.stageX, event.stageY);
      const pose = new ROSLIB.Pose({
        position: new ROSLIB.Vector3(coords)
      });
      // send the goal
      sendGoal(pose);
      this.dispatchEvent(new CustomEvent('newposition', { detail: pose.position }));
    }
    

    and then add an extra line in your React component:

    canvas.addEventListener("newposition", (e) => setValue(e.detail), false);
    

    In both cases, you will need to add value as a second argument to useEffect to prevent re-renders.