【Android】rememberUpdateStateを使おう

### はじめに
先日の記事でwebViewでの動画のシークを取り上げましたが、その続き。

https://qiita.com/mikiFlag/items/a5a853d541e56da12fec

`doUpdateVisitedHistory()`を使って動画の再生開始位置を指定する方法を載せました。
これを利用して秒数を入力して、動画開始時の再生位置を自由に操作できるようにしようとしました。

#### 作成コード（問題がある例）
```kotlin
@SuppressLint("SetJavaScriptEnabled")
@Composable
private fun BrowserWebView(url: String, seekTime: Double = 0.0) {
    val isInspecting = LocalInspectionMode.current
    var webView: WebView? by remember { mutableStateOf(null) }
    Column {
        AndroidView(
            modifier = Modifier.clipToBounds().weight(1f),
            factory = {
                WebView(it).apply {
                    if (!isInspecting) {
                        settings.javaScriptEnabled = true
                        settings.domStorageEnabled = true
                    }
                    layoutParams = ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT
                    )
                    webViewClient = object : WebViewClient() {
                        override fun doUpdateVisitedHistory(
                            view: WebView?,
                            currentUrl: String?,
                            isReload: Boolean,
                        ) {
                            super.doUpdateVisitedHistory(view, currentUrl, isReload)
                            view?.evaluateJavascript(Script.seekToTime(seekTime), null)
                        }
                    }
                    webChromeClient = WebChromeClient()
                    loadUrl(url)
                }
            },
            update = { newWebView ->
                if (isInspecting) return@AndroidView
                webView = newWebView
            }
        )
    }
}

// SeekButton, Script.seekToTimeは前回の記事を参照のこと（省略）

@Composable
@Preview
private fun BrowserWebViewScreen() {
    var seekTime by remember { mutableDoubleStateOf(0.0) }
    var value: String by remember { mutableStateOf("") }
    Column {
        // 秒数を入力する欄
        // サンプルです
        TextField(
            value = value,
            onValueChange = { value = it },
            modifier = Modifier
                .fillMaxWidth()
                .padding(10.dp),
            placeholder = { Text("動画開始時に遷移させたい秒数を入力") },
            keyboardOptions = KeyboardOptions.Default.copy(
                imeAction = ImeAction.Done,
                keyboardType = KeyboardType.Text,
            ),
            keyboardActions = KeyboardActions(
                onDone = {
                    seekTime = value.toDoubleOrNull() ?: 0.0
                    Log.d("testComment", "seekTime: $seekTime")
                }
            ),
            singleLine = true,
        )
        BrowserWebView(url = "https://www.youtube.com/", seekTime = seekTime)
    }
}
```

しかし、これだとうまく動作せず、秒数を指定しても動画は最初から再生されてしまいます。

### **問題の原因**
`doUpdateVisitedHistory` メソッドの `evaluateJavascript` で渡している `seekTime` は `BrowserWebView` の初回コンポーズ時の値で固定されていて、再コンポーズが発生しても更新されません。

### **解決策：`rememberUpdatedState` を使用する**
ではどうするか。
Jetpack Compose では `rememberUpdatedState` を使用すると、外部から渡された値を適切に更新できます。

#### **修正後のコード**
```kotlin
@SuppressLint("SetJavaScriptEnabled")
@Composable
private fun BrowserWebView(url: String, seekTime: Double = 0.0) {
    val isInspecting = LocalInspectionMode.current
    var webView: WebView? by remember { mutableStateOf(null) }
    val updateSeekTime by rememberUpdatedState(seekTime) // 外からもらってきた値をrememberする
    Column {
        AndroidView(
            modifier = Modifier.clipToBounds().weight(1f),
            factory = {
                WebView(it).apply {
                    if (!isInspecting) {
                        settings.javaScriptEnabled = true
                        settings.domStorageEnabled = true
                    }
                    layoutParams = ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT
                    )
                    webViewClient = object : WebViewClient() {
                        override fun doUpdateVisitedHistory(
                            view: WebView?,
                            currentUrl: String?,
                            isReload: Boolean,
                        ) {
                            super.doUpdateVisitedHistory(view, currentUrl, isReload)
                            view?.evaluateJavascript(Script.seekToTime(updateSeekTime), null)
                        }
                    }
                   ... // 以下省略
```

`rememberUpdatedState(seekTime)` を使用すると、`seekTime` の最新の値を `doUpdateVisitedHistory` で参照できるようになります。
これにより、外部から `seekTime` を変更した際に、適切に再生開始位置が更新されるようになります。

### **まとめ**
Jetpack Compose の `WebViewClient` 内の関数をoverrideするときは、`rememberUpdatedState` を使用する必要がある場合もある、ということを覚えておきたいですね。
もちろんそれ以外でも`rememberUpdatedState` が有用なケースはあると思うので気をつけていきたいです。