p-lr / MapCompose

A fast, memory efficient Jetpack Compose library to display tiled maps, with support for markers, paths, and rotation.
Apache License 2.0
220 stars 19 forks source link

Problem: Click callback uses outdated data #106

Closed cwsiteplan closed 8 months ago

cwsiteplan commented 8 months ago

Hi,

i ran into a strange issue using the marker click callback.

Pushed a sample here: https://github.com/p-lr/MapCompose/compare/master...cwsiteplan:MapCompose:bug/marker-click-callback

I'm adding a click callback to the mapState initially and inside the callback i do access data that does change over time. But somehow the callback accesses only the initially passed object.

Interestingly, moving the code outside the child composable it starts to work fine 🤔

Probably not related to your implementation at all, but would be glad if you have an idea why.

p-lr commented 8 months ago

You probably need to update the callback when your state changes. Try with:

@Composable
fun MyMapComposable(modifier: Modifier = Modifier, myData: MyData, mapState: MapState) {
    LaunchedEffect(myData) {
        mapState.onMarkerClick { id, x, y ->
            Log.d("Bug", "my marker state: ${myData.someList.size}")
        }
    }

    MapUI(modifier, state = mapState)
}
cwsiteplan commented 8 months ago

yep, that works too - but i'm not quite sure why i would need to do that .

shouldn't the lambda access the updated data anyways? (data is correctly updated outside of the callback)

p-lr commented 8 months ago

That's because LaunchedEffect can take keys as parameters, and whenever one keys changes of value, the composable block is recomposed. By using LaunchedEffect(Unit){..}, you are actually capturing the initial value of your state.

cwsiteplan commented 8 months ago

thanks, i thought i'm aware of the concept of LaunchedEffect but assumed that the created callback (lambda) would still access the current state - seems it also captures a snapshot of the current state -interesting.

p-lr commented 8 months ago

Another approach is to call a method from the view-model from the lambda, passing id, x and y values. Since the view-model has all necessary data, you can do necessary operations and state changes from there, without having to worry about updating the lambda.