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.
I need to provide a clean-up function to
useEffect()to remove thedraw.createhandler whenever thedefineFeaturestate variable changes, as recommended at the end of this thread. Without removing thedraw.createfunction each timedefineFeaturechanges, we get multiple closures ofdraw.create, leading to the strange behavior I observed.To clean up the
draw-createfunction, place it in a separate function, which can reside either inside or outside theuseEffect(). I named the functionprocessDrawFeature()and I placed it at the top of theuseEffect()block. Then, at the bottom of theuseEffect()block, I first bound thedraw,createhandler to it:Then, on the last line of the
useEffect()block, I defined the cleanup function:After making these changes, the
draw.createis always bound to a closure ofprocessDrawnFeaturethat has the most recent value of thedefineFeaturestate variable. Hooray!