Pass by Value vs CompositionLocal vs Static CompositionLocal
Tags: android, kotlin, beginners, jetpackcompose
Published: 2023-05-06 00:00:00

**Examples to show how to pass data to composable functions using function parameters(i.e. pass by value), CompositionLocal and static CompositionLocal**

There are a few ways you can pass data to a composable function:

* Pass by Value (function parameter)
    
* CompositionLocal
    
* Static CompositionLocal
    

**Pass by Value is a conventional way. CompositionLocal and static CompositionLocal is a Jetpack Compose way,** but **static CompositionLocal is useless** in my opinion (will be explained later).

## Pass by Value

This is a very simple example to pass `counter` value to the `Parent()` composable function, then increments it by 1 and passes it to the `Child()` composable function. Finally, it calls the `GrandChild()` composable function without any parameters.

Let's investigate the code, what do you think the Logcat outputs are during the first composition and the subsequent recomposition?

```kotlin
private val tag = "CompLocal"

@Composable
fun PassByValueDemo() {

    var counter by remember {
        mutableStateOf(-1)
    }

    MyButton(onClick = { ++counter }, text = "PassByValue Demo")

    if(counter < 0) return

    Log.d(tag, "************** Pass by Value **************")
    Parent(counter)
}

@Composable
private fun Parent(value: Int) {
    Log.d(tag, "Start Parent - value: $value")
    Child(value + 1)
    Log.d(tag, "End Parent - value: $value")
}

@Composable
private fun Child(value: Int) {
    Log.d(tag, "Start Child - value: $value")
    GrandChild()
    Log.d(tag, "End Child - value: $value")
}


@Composable
private fun GrandChild() {
    Log.d(tag, "Start GrandChild")
    Log.d(tag, "End GrandChild")
}
```

**Logcat Output - First Composition (first clicked)**

`value` is incremented by 1 and passed into the `Child()` composable

```plaintext
************** Pass by Value **************
Start Parent - value: 0
Start Child - value: 1
Start GrandChild
End GrandChild
End Child - value: 1
End Parent - value: 0
```

**Logcat Output - Recomposition (second clicked)**

A very important thing to notice is the `GrandChild()` **composable is skipped**.

```plaintext
************** Pass by Value **************
Start Parent - value: 1
Start Child - value: 2
End Child - value: 2
End Parent - value: 1
```

## CompositionLocal

To accomplish the exact behavior, `CompositionLocal` can be used.

Here are the simple steps:

1. **Create a** `CompositionLocal` **variable** (using `compositionLocalOf()` that is accessible from the compostable functions that you want to use it.
    
    ```kotlin
    private val LocalInt = compositionLocalOf { 0 }
    ```
    
2. **Provide value to the** `CompositionLocal` (i.e. `LocalInt`) using `CompositionLocalProvider`.
    
    ```kotlin
    CompositionLocalProvider(
        LocalInt provides 0,
    ) {
        //call your composable function here
    }
    ```
    
    * `LocalInt provides 0` is similar to `LocalInit.provides(0)`. It is a [Kotlin infix notation](https://vtsen.hashnode.dev/kotlin-infix-notation-is-confusing).
        
3. **Access the** `CompositionLocal`'s value by `CompositionLocal.current`.
    
    ```kotlin
    LocalInt.current
    ```
    

The full code looks like this

```kotlin
private val LocalInt = compositionLocalOf { 0 }
private val tag = "CompLocal"

@Composable
fun CompositionLocalDemo() {

    var counter by remember {
        mutableStateOf(-1)
    }

    MyButton(onClick = { ++counter }, text = "CompositionLocal Demo")

    if(counter < 0) return

    Log.d(tag, "************** Using CompositionLocal **************")
    CompositionLocalProvider(
        LocalInt provides counter,
    ) {
        Parent()
    }
}

@Composable
private fun Parent() {
    Log.d(tag, "Start Parent - LocalInt: ${LocalInt.current} ")

    CompositionLocalProvider(
        LocalInt provides LocalInt.current + 1,
    ) {
        Child()
    }

    Log.d(tag, "End Parent - LocalInt: ${LocalInt.current}")
}

@Composable
private fun Child() {
    Log.d(tag, "Start Child - LocalInt: ${LocalInt.current} ")

    GrandChild()

    Log.d(tag, "Emd Child - LocalInt: ${LocalInt.current} ")
}

@Composable
private fun GrandChild() {
    Log.d(tag, "Start GrandChild")

    Log.d(tag, "End GrandChild")
}
```

This has the same outputs as the Pass by Value example above.

**Logcat Output - First Composition (first clicked)**

```plaintext
************** Pass by Value **************
Start Parent - value: 0
Start Child - value: 1
Start GrandChild
End GrandChild
End Child - value: 1
End Parent - value: 0
```

**Logcat Output - Recomposition (second clicked)**

```plaintext
************** Pass by Value **************
Start Parent - value: 1
Start Child - value: 2
End Child - value: 2
End Parent - value: 1
```

## Static CompositionLocal

You can replace `CompositionLocal` with static `CompositionLocal`. This code

```kotlin
private val LocalInt = compositionLocalOf { 0 }
```

is replaced by

```kotlin
private val LocalInt = staticCompositionLocalOf { 0 }
```

and everything remains the same.

However, the **outputs are NOT the same as Pass by Value and** `CompositionLocal`. Changes to the `CompositionLocal`'s value **triggers the entire composition tree to be recomposed**.

**Logcat Output - First Composition (first clicked)**

```plaintext
************** Pass by Value **************
Start Parent - value: 0
Start Child - value: 1
Start GrandChild
End GrandChild
End Child - value: 1
End Parent - value: 0
```

**Logcat Output - Recomposition (second clicked)**

```plaintext
************** Pass by Value **************
Start Parent - value: 1
Start Child - value: 2
Start GrandChild
End GrandChild
End Child - value: 2
End Parent - value: 1
```

As you can see, `GrandChild()` composable function is called/recomposed even though it doesn't access the `LocalInt.current` value. This is a complete waste of unnecessary recompositions in my opinion.

The official document states that you should only use `staticCompositionLocalOf()` for a value that doesn't change. But the issue is, **how do you prevent the user or any developer from changing it?** You can't.

Therefore, it seems to be we should just use `CompositionLocalOf()` and NOT use `staticCompositionLocalOf()` as a best practice.

> The official document does mention about performance benefits of using `staticCompositionLocalOf` if the value is not changed, but how much benefits exactly?
> 
> I agree to use `staticCompositionLocalOf()` only if it is a constant value and can't be changed. Then, this prevents the users from misusing it. What do you think?

## Conclusion

`CompositionLocal` is just a Jetpack Compose way as a replacement of passing by value to a composable function. This may be helpful if you have a global variable that is often being used by your composable functions.

Static `CompsitionLocal` triggers the entire composable tree to be recomposed if its value is changed. So, use it carefully. My recommendation is, don't use it.

## Source Code

GitHub Repository: [**Demo\_UnderstandComposeConcept**](https://github.com/vinchamp77/Demo_UnderstandComposeConcept)

---

*Originally published at [https://vtsen.hashnode.dev](https://vtsen.hashnode.dev/pass-by-value-vs-compositionlocal-vs-static-compositionlocal)*.