要約
kotlin-react-routerを6.3.0-pre491から6.7.0-pre492にする段階でRoute
関数のラムダ式引数内でpath
やelement
が使えなくなるが、this as PathRouteProps
でキャストすれば回避できる。
さらに先のバージョン(6.17.0-pre635)ではこの回避策も無効になる(続編参照)。
環境
- Ubuntu 20.04LTS
- IntelliJ IDEA 2023.1.4 (Community Edition)
- Kotlin/JS 1.8.20(IRコンパイラ)
- Kotlin Wrappers 1.0.0-pre.598
- kotlin-react-router-dom 6.14.1-pre.598
- gradle 8.1.1
動機
2023年7月現在Kotlin Wrappersに、"React wrappers in particular heavily relied on specific characteristics of the default backend, which might make your current code incompatible with the new IR backend."と書かれているので、LEGACYコンパイラのままにしてきた。しかし、Kotlin 1.9がリリースされLEGACYコンパイラが廃止される流れになっているので、コンパイラの切り替えとライブラリの更新を一気に行おうと思った。
現象
Kotlin Wrappersを1.0.0-pre.348から1.0.0-pre.598に更新し、build.gradle.ktsでコンパイラの指定をLEGACY
からIR
に変更した。これに合わせてkotlin-react-router-domのバージョンも6.3.0-pre.348から6.14.1-pre.598に変更した。
ソースコードの変更はimport react.dom.html
をimport web.html
に変更するといった対応で済むものが多かったが、React Routerに関しては
import react.FC
import react.Props
import react.router.Route
import react.router.Routes
import react.router.dom.BrowserRouter
val Main = FC<Props> {
BrowserRouter {
Routes {
Route {
path = "sample.html"
element = createElement(Sample)
}
}
}
}
のように書いて問題なく動作していたのが、path
やelement
の所でエラーが出るようになった。
調査
Kotlin Wrappersのソースコード
path
やelement
はreact.router.RouteProps
由来とのことで、該当部分のソースを参照すると、
sealed external interface RouteProps : react.Props /* export type RouteProps = PathRouteProps | LayoutRouteProps | IndexRouteProps; */
となっている。これが、ライブラリ更新前だと、
external interface RouteProps : react.PropsWithChildren {
var caseSensitive: Boolean?
override var children: react.ReactNode?
var element: react.ReactNode?
var index: Boolean?
var path: String?
}
となっており、更新に際して属性の指定が消されてしまっている。
React Routerのソースコード
これだけだと解決策が浮かばないのでReact Routerのソースを探ってみた。
export type RouteProps = PathRouteProps | LayoutRouteProps | IndexRouteProps;
はKotlin側のソースでコメントアウトされていた通り。そしてPathRouteProps
, LayoutRouteProps
, IndexRouteProps
はそれぞれ、
-
PathRouteProps
:path
やelement
がある。 -
LayoutRouteProps
:PathRouteProps
と同等。 -
IndexRouteProps
:PathRouteProps
のindex
をtrueにしたもの。
といった状態であった。ちなみにPathRouteProps
に対応してPathRoute
がある訳ではなく、あるのはRoute
だけの模様。
推定される原因と解決法
RouteProps
の属性情報が消された上に、PathRouteProps
, LayoutRouteProps
, IndexRouteProps
のどれであるか特定する方法がないので、path
やelement
にアクセスできない。
逆に言うとPathRouteProps
, LayoutRouteProps
, IndexRouteProps
のどれであるか特定できればpath
やelement
にアクセスできるようになりそう。
解決
Route
の型引数にPathRouteProps
を指定して解決を図ろうとしたが、うまく行かなかった。以下のようにthis as PathRouteProps
でキャストを強制したらこれまで通り動作するようになった。
import react.FC
import react.Props
import react.router.PathRouteProps
import react.router.Route
import react.router.Routes
import react.router.dom.BrowserRouter
@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE")
val Main = FC<Props> {
BrowserRouter {
Routes {
Route {
this as PathRouteProps
path = "sample.html"
element = createElement(Sample)
}
}
}
}
IndexRouteProps
前述の通りreact.router.IndexRouteProps
というものがあるが、index = true
のRoute
でこれにキャストしてもうまく行かなかった。
代わりに上記PathRouteProps
にキャストした後、path
を設定せずにindex = true
としたら問題なかった。
考察
ライブラリを更新しないという選択も
ちなみに、kotlin-react-router-domのバージョンを6.3.0に据え置いたままコンパイラをIRにして他のReact関連のラッパーを更新しても、自分の使用している範囲では問題がなかった。
いちいちキャストが必要というのは明らかに使いづらいので、もしかしたら改善されるかもしれない。という希望的観測のもと、もうしばらく放置しておくというのは必ずしも悪い考えではないと思う。
発生時期など
GitHubの履歴によると、2023年2月上旬の6.3.0-pre.491から6.7.0-pre.492への変更でフォルダ構成と共に色々変わってこうなったようだ。
kotlin-react-router-domの以前のreadme.mdにはサンプルコードが載っていたのだが、上記更新の際に消えてしまっている。発生する問題の説明がないのも頂けないが、せめて動作するサンプルコードくらい載せてくれればここまで悩まずに済んだ。
自動生成のコードのようなので、開発側も自分自身で使っていないのかもしれない。
改訂履歴(表現の修正などは除く)
- 2023/5/14: 挫折してしばらく放置
- 2023/7/23: ライブラリの更新に成功
- 2023/7/24: 初版作成
- 2024/8/4: 続編へリンク