2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Jetpack Composeで、Mutable系統をStateに登録したらあかん

Last updated at Posted at 2021-09-16

Mutable系統をStateに入れていて、数日ドツボにはまったのでメモしておきます。

問題

Jetpack Composeの勉強のために、以下のようなアプリを作成しました。

スクリーンショット 2021-09-16 221859.png

ソースコードは以下の通りです。

List.kt
@Composable
fun List(){
    
    //表示するStateを作成
    val list by remember { mutableStateOf((0..3).toMutableList()) }

    LazyColumn(Modifier.fillMaxSize()){
        
        //カラムを表示する部分
        item{
            for(i in list){
                LazyCard(text = i.toString())
            }
        }
        
        //ボタンを表示する部分
        item { 
            AddButton(onClick = {
                list.add(list.size)
            })
        }
    }
}

これでボタンを押せば、カラムが増えていくはず・・・ですがなぜか増えません。

解決策?

原因ですが、おそらく

List.kt
        //ボタンを表示する部分
        item { 
            AddButton(onClick = {
                list.add(list.size)
            })
        }

ここでadd関数を使っていますが、これでは差分を検知してくれないからではないかと思います。

そこでこの部分をとりあえず以下のように書き直してみました。

List.kt

    //表示するStateを作成
    val list by remember { mutableStateOf((0..3).toMutableList()) }

    ・・・

        //ボタンを表示する部分
        item { 
            AddButton(onClick = {
                val tempList = list
                tempList.add(list.size)
                list = tempList
            })
        }

これで、listに再代入しているので差分を検知してくれるはず・・・がまたしてもピクリともしない。

原因

なぜ検知してくれないのかと思い、tempListに要素を追加したタイミングでそれぞれの値を見てみました。

List.kt

        //ボタンを表示する部分
        item { 
            AddButton(onClick = {
                val tempList = list
                tempList.add(list.size)
                Log.d("debug", "list: $list, tempList: $tempList")
                list = tempList
            })
        }

すると驚くことに、以下の結果が出力されました。

D/debug: list: [0, 1, 2, 3, 4], tempList: [0, 1, 2, 3, 4]

これはつまり

tempList.add(list.size)

が実行されたタイミングでlistの方にも反映されているということです。
ということは

val tempList = list

ここでの代入は値渡しではなく、参照渡しになっている?ということでしょう。

確かにそう考えると、add関数では差分を検知しないし、最後の代入でも差分がない。
UIの再描写が走らないはずです。

解決策

GoogleのCodelabを見て回っていたら、このような感じで記述していました。

List.kt

    var list by remember { mutableStateOf((0..3).toList()) }

    ・・・

        //ボタンを表示する部分
        item { 
            AddButton(onClick = {
                list = list + listOf(list.size)
            })
        }

なるほど・・・完全に新しく作り直していますね。
確認したところ、これだと正しく動作しました。

となってくると、StateでMutable系統を使うのはやめた方がよさそうですね。
add関数が使えない時点で通常の型の代わりに使うメリットがないし、むしろバグの温床になりそうです。

補足

参照渡しになるのはKotlin(正確にはJava)の仕様のようですね。
プリニティブ型では値渡し、クラス型では参照渡しをするようで、Mutable系統はもちろんクラス型なので参照渡しになるということのようです。

参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?