ぺんぎんメモ

プログラミングのメモです。たまに私生活のことや鬱っぽいことを書きます。

2021年09月03日 Jetpack Compose

【18:48追記】
今日は天下一品で1,098円のチャーハンセットを食べた。
【追記終わり】

今回の記事は、日記というよりも Jetpack Compose まとめ。

Jetpack Compose は、React を使うフロントエンド開発者が手軽に Android ネイティブアプリ開発者へと移行できるフレームワーク。1ヶ月ほど前に安定版になったため、これからは Jetpack Compose を使ったプロジェクトも増えていくと思う。

React と Jetpack Compose の比較

参考サイト : React to Jetpack Compose RC Dictionary

非同期な useEffect > LaunchedEffect

useEffect のコールバック関数は非同期関数に出来ないため、非同期な処理を行う場合はコールバック関数内で非同期関数を即時実行する処理を書く必要がある。
更に、処理中の非同期処理はフックの依存配列が変更されたときにキャンセルしなければならないため、キャンセルの処理も書く必要がある。
つまり次のようなコードになる。

useEffect(() => {
  let unmounted = false;
  (async () => {
    const user = await getUser(id);
    if (unmounted) {
      return;
    }
    setUser(user);
  })();
  return () => {
    unmounted = true;
  };
}, [id]);

Jetpack Compose では次の3行で済む。前回の非同期処理はキャンセルされる。

LaunchedEffect(id) {
    user.value = getUser(id).await()
}

注意点として、LaunchedEffect には必ずキーを渡さなければならない。つまり、React の useEffect(() => { ... }, []) みたいに LaunchedEffect {} と書くことはできない。代わりに LaunchedEffect(Unit) {} と書く。

useState と useEffect > produceState

先程の Kotlin コードを user の定義も含めて書くと次のようになる。

val user = remember { mutableStateOf<User?>(null) }
LaunchedEffect(id) {
    user.value = getUser(id).await()
}

これは、produceState を使えばもう少し簡潔に書ける。

val user = produceState<User?>(null, id) {
    value = getUser(id).await()
}

value はどこから来たんだろう?という気持ちになる。

JSX 内の map > for

Jetpack Compose では、React の JSX にあたる部分に for を書くことができる。

val counts = arrayOf(1, 2, 3)
for (count in counts) {
    Text(count.toString()) // コンポーネント
}

JSX では必然的に関数型っぽく書く必要があった。
その制限が Jetpack Compose にはない。

key 属性 > key 関数

React では最適化のために key 属性が使われる。

users.map(user => (
  <UserInfo key={user.id} user={user} />
))

Jetpack Compose では key 関数を使って次のように書く。

for (user in users) {
    key(user.id) { Text(user.name) }
}

コンポーネントとフック > @Composable な関数

React ではコンポーネントとフックは別物だが、Jetpack Compose ではひとつの関数に両方の機能を持たせられる。つまり次のように書ける。

@Composable
fun hookAndComponent(): State<Int> {
    val count = remember { mutableStateOf<Int>(0) }
    Text("hello, world")
    return count
}

@Component ではなく @Composable な理由はここにあると思う。@Composable な関数はコンポーネントとしてもフックとしても扱える。参考サイトでは key 関数のことを「key Composable」と呼ばれている。これにならって、上記の hookAndComponent() は hookAndComponent コンポーザブルと呼ぶと良さそう。

おわりに

次は GlobalScope について勉強しようと思う