What do you guys think about a non-inlined `forEac...
# compose
a
What do you guys think about a non-inlined
forEach
for
@Composable
functions?
Copy code
/**
 * Like Kotlin's [Iterable.forEach], but the action is not inlined, and is [Composable].
 * This makes it better when used in a [Composable] function when each iteration accesses a [State]. When using the
 * regular, inlined `forEach`, whenever one of the states accessed changes, the entire function (and the entire loop)
 * will be recomposed. Whereas with a non-inlined `forEach`, only the iteration with the state that changed will be
 * recomposed.
 */
@Composable
fun <T> Iterable<T>.forEachComposable(action: @Composable (T) -> Unit) {
    for (element in this) action(element)
}
a
whether it saves more than it costs at any particular site of use is going to be pretty subtle. There's a big difference between how non-capturing, stable-capturing, and unstable-capturing lambdas passed as
action
behave, and introducing two layers of composable function calls (
forEachComposable
plus
action
in each iteration) isn't free either. Any time you use this you're placing a bet that whatever
action
is doing is expensive enough to make it worth it, and that the composables
action
calls aren't themselves eligible for skipping, which would optimize the invalidation case that
forEachComposable
looks to solve.
a
Unfortunately the tools to control, understand and profile recompositions are limited, which makes me somewhat paranoid.
Here’s a simplified version of what made me think about this `forEachComposable`:
Copy code
class Counter(
    val number: Int
){
    fun next() = Counter(number+1)
}

val counters = (1..20).map { MutableStateFlow(Counter(0)) }.toMutableList()

fun main() {
    singleWindowApplication(
        title = "Text",
        state = WindowState(
            width = 800.dp,
            height = 400.dp,
        )
    ) {
        Column (
            modifier = Modifier.fillMaxWidth()
        ){
            for (i in counters.indices){
                val counter by counters[i].collectAsState()
                ListItem(
                    counter = counter,
                    modifier = Modifier
                        .recomposeHighlighter()
                        .clickable {
                            counters[i].value = counter.next()
                        }
                )
            }
        }
    }
}


@Composable
fun ListItem(
    counter: Counter,
    modifier: Modifier
){
    Text(
        text = "Count: ${counter.number}",
        modifier = modifier
    )
}
Each click causes the entire list to highlight recompositions.