1

I am using mapbox-gl-draw in a React.js app to facilitate path creation on a Mapbox canvas. Since I'm in React, I have to declare the draw.create event handler within a useEffect() block. Since my draw.create handler depends on a state variable, I declare the variable in the dependency list at the end of the useEffect() block. Here is the essence of the useEffect() block, with two debugging statements added to try to understand the behavior:

useEffect(() => {
    console.dir("In useEffect to initialize draw_create...");
    /* POINT 1 */
    if (defineFeature === null) {
      console.dir("defineFeature is null");
    } else {
      console.dir("Value of defineFeature: " + defineFeature.holeNum + ", " + 
      defineFeature.featureType);
    }
           
      map.current.on('draw.create', ()=> {
        /* POINT 2 */
        if (defineFeature === null) {
          console.dir("defineFeature is null");
        } else {
          console.dir("Value of defineFeature: " + defineFeature.holeNum + ", " + 
          defineFeature.featureType);
        }
        /* Code to process the line is omitted */
  },[defineFeature]); 

When I execute this code, the value of defineFeature is as expected at POINT 1; it is the most recent value of defineFeature. However, the value of defineFeature at POINT 2, within draw_create, is a different matter. After a user double-clicks to terminate a line_string on the map canvas, the draw.create handler is fired multiple times under different closures! The first time draw.createfires, defineFeature is null, which is its value when the map first initializes. The last time draw.create fires, the value of defineFeature is correct. However, at that point, it's too late; the function has already tried to process the line with an incorrect (stale) value of defineFeature.

The number of times that draw.create fires when the user double-clicks to terminate a LineString is not predictable. From what I can gather, it seems to depend on the number of previous closures of draw.create. Indeed, the value of defineFeature in each invocation seems to be different, aligning with its previous values.

Can anyone explain this behavior--and, more importantly, how to fix it? In draw.create, I need to be able to use the value of defineFeature under the most recent closure.

SpeedGolfer
  • 255
  • 3
  • 13

1 Answers1

0

I need to provide a clean-up function to useEffect() to remove the draw.create handler whenever the defineFeature state variable changes, as recommended at the end of this thread. Without removing the draw.create function each time defineFeature changes, we get multiple closures of draw.create, leading to the strange behavior I observed.

To clean up the draw-create function, place it in a separate function, which can reside either inside or outside the useEffect(). I named the function processDrawFeature() and I placed it at the top of the useEffect() block. Then, at the bottom of the useEffect() block, I first bound the draw,create handler to it:

 map.current.on('draw.create', processDrawnFeature);

Then, on the last line of the useEffect() block, I defined the cleanup function:

return () => map.current.off('draw.create', processDrawnFeature);

After making these changes, the draw.create is always bound to a closure of processDrawnFeature that has the most recent value of the defineFeature state variable. Hooray!

SpeedGolfer
  • 255
  • 3
  • 13