pushpalroy / JetLime

A Kotlin Multiplatform library to display a timeline view in Android. 🍋
https://jetlime.pushpalroy.com
MIT License
431 stars 25 forks source link

Possible recomposition issue #8

Closed joreilly closed 2 years ago

joreilly commented 2 years ago

Hi, I'm having issue where UI isn't updating when I create new JetLimeItemsModel in response to some data being updated...I have made changes locally that seems to fix it but not sure if there are other ramifications of making that change.

So, firstly this is more or less what I have

val listState = rememberLazyListState()

val busInfoList by viewModel.busInfoList.collectAsState(emptyList())
val busStopList by viewModel.allBusStops.collectAsState(emptyList())

val jetLimeItemsModel by derivedStateOf {
        val jetItemList: MutableList<JetLimeItemsModel.JetLimeItem> = mutableStateListOf()

        if (busInfoList.isNotEmpty()) {
            val bus = busInfoList.find { it.vehicle_id == vechicleId }
            val nextStopRef = bus?.next_stop_ref

            val busList: List<String> = bus?.route ?: emptyList()
            busList.forEach { busStopRef ->
                val busStop = busStopList.find { it.stopRef == busStopRef }

                val busStopIndex = busList.indexOf(busStopRef)
                val nextBusStopIndex = busList.indexOf(nextStopRef)

                val jetLimeItemConfig = if (busStopRef == nextStopRef)
                    JetLimeItemConfig(iconAnimation = IconAnimation())
                else {
                    if (busStopIndex < nextBusStopIndex) {
                        JetLimeItemConfig(itemHeight = 80.dp, iconType = IconType.Checked)
                    } else {
                        JetLimeItemConfig(itemHeight = 80.dp, iconType = IconType.Empty)
                    }

                }

                jetItemList.add(
                    JetLimeItemsModel.JetLimeItem(
                        title = busStop?.shortName ?: "",
                        description = busStopRef,
                        jetLimeItemConfig = jetLimeItemConfig
                    )
                )
            }
        }
        JetLimeItemsModel(jetItemList)
    }

JetLimeView(
    jetLimeItemsModel = jetLimeItemsModel,
    jetLimeViewConfig = JetLimeViewConfig(lineType = LineType.Solid),
    listState = listState
)

I'm seeing that when nextStopRef changes (with resultant change to model) the UI is not updating (to show animation for that stop). Issue seems to be related to following code in JetLineView.

val jetLimeItem = remember { mutableStateOf(item) }

....

          JetLimeItemView(
            title = jetLimeItem.value.title,
            description = jetLimeItem.value.description,
            content = jetLimeItem.value.content,
            itemConfig = jetLimeItem.value.jetLimeItemConfig.apply { position = pos },
            viewConfig = jetLimeViewConfig,
            totalItems = jetLimeItemsModel.items.size
          )

If I replace jetLimeItem.value with just item above then recomposition seems to work correctly when the model value changes.

pushpalroy commented 2 years ago

@joreilly Thanks for raising this bug. 🌮

The issue was due to remembering the JetLimeItem which was basically making the composable stateful rather than stateless. Thanks for pointing the issue out!

The issue is now fixed in v1.0.3.

Also added:

Here is the sample: https://user-images.githubusercontent.com/19844292/153912686-93b9d098-8bc8-4a7a-b969-cc598273d564.mp4