🐻‍❄️

Jetpack Compose の SideEffect の動作を調べる

2022/02/22に公開

SideEffect とは

SideEffect コンポーザブルは再コンポーズのたびに呼び出される。

@Composable
@NonRestartableComposable
@OptIn(InternalComposeApi::class)
fun SideEffect(
    effect: () -> Unit
) {
    currentComposer.recordSideEffect(effect)
}

特徴

  • SideEffect が再コンポーズされたときに SideEffect に渡した effect が実行される
  • 再コンポーズされたとき Compose が管理していないオブジェクトに対して状態を共有するのに使われる。

どんなときに役立つか?

例えば Logger を宣言してユーザーごとのログを記録したい場合があるとします。この場合は Compose の User の状態に合わせて Logger の User の状態も更新する必要があります。こういった場合に SideEffect を利用すると Compose の User に Logger の User を更新できます。

object Logger {
    var user: String = "UNKNOWN"

    fun write(message: String) {
        Log.v("TEST", "$message $user")
    }
}

@Composable
fun rememberLogger(userName: String): Logger {
    val logger: Logger = remember { Logger }
    SideEffect {
        logger.user = userName
    }
    return logger
}

サンプル

Compose の User に Logger の User が更新できているか確認するサンプルを作成してみます。

  • 「ユーザー変更」「アクション実行」のログを記録する
  • 「ユーザー変更」時には一覧からランダムでユーザーを選択する
  • 「アクション実行」時は特に何もしないようにしておく
private val Users = listOf(
    "ONE",
    "TWO",
    "THREE",
    "FOUR"
)

@Composable
fun SideEffectSample() {
    var user by remember { mutableStateOf(Users.random()) }
    val logger = rememberLogger(userName = user)

    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "Side Effect Sample")
            Text(text = "User $user")
            Button(onClick = {
                logger.write("Change User")
                user = Users.random()
            }) {
                Text(text = "Change User")
            }
            Button(onClick = {
                logger.write("Execute Action")
            }) {
                Text(text = "Execute Action")
            }
        }
    }
}

動作確認

サンプルで動作確認してみると以下のように動作します。

  • 入場時

    • user が宣言されたとき、一覧からランダムでユーザーが選択される
    • logger が宣言されたとき、インスタンスを取得してインスタンスのユーザーを更新する
  • ユーザー変更が実行されたとき

    • 一覧からランダムでユーザーを取得し、user を更新する
    • user が更新されるとコンポーザブルが再コンポーズされる、

    そのときにあわせて SideEffect が実行されて logger のユーザーも更新される。

SideEffectDemo.gif

2022-02-22 14:26:36.469 11962-11962/jp.kaleidot725.sample V/TEST: Execute Action FOUR
2022-02-22 14:26:37.264 11962-11962/jp.kaleidot725.sample V/TEST: Execute Action FOUR
2022-02-22 14:26:38.420 11962-11962/jp.kaleidot725.sample V/TEST: Change User FOUR
2022-02-22 14:26:39.803 11962-11962/jp.kaleidot725.sample V/TEST: Execute Action ONE
2022-02-22 14:26:40.604 11962-11962/jp.kaleidot725.sample V/TEST: Execute Action ONE
2022-02-22 14:26:41.396 11962-11962/jp.kaleidot725.sample V/TEST: Change User ONE
2022-02-22 14:26:42.488 11962-11962/jp.kaleidot725.sample V/TEST: Execute Action TWO
2022-02-22 14:26:43.083 11962-11962/jp.kaleidot725.sample V/TEST: Execute Action TWO

参考文献

Discussion