Skip to content

react-router を使う

react-router を使ってページ遷移を実現します。

セットアップ

kotlin
plugins {
    kotlin("multiplatform") version "2.0.10"
}

kotlin {
    js {
        browser {
            binaries.executable()
        }
    }
    sourceSets {
        val jsMain by getting {
            dependencies {
                implementation(project.dependencies.platform("org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom:1.0.0-pre.795"))
                implementation("org.jetbrains.kotlin-wrappers:kotlin-emotion")
                implementation("org.jetbrains.kotlin-wrappers:kotlin-react")
                implementation("org.jetbrains.kotlin-wrappers:kotlin-react-dom")
                implementation("org.jetbrains.kotlin-wrappers:kotlin-react-router-dom")
            }
        }
    }
}

コード

kotlin
package org.example

import emotion.react.css
import js.errors.JsError
import react.Component
import react.FC
import react.Fragment
import react.Props
import react.create
import react.dom.client.createRoot
import react.dom.html.ReactHTML.div
import react.router.RouteObject
import react.router.RouterProvider
import react.router.dom.NavLink
import react.router.dom.createHashRouter
import react.router.useRouteError
import web.cssom.Display
import web.cssom.JustifyContent
import web.cssom.NamedColor
import web.cssom.None
import web.cssom.path
import web.cssom.px
import web.dom.document
import web.prompts.alert

val NavBar =
    FC {
        div {
            css {
                display = Display.flex
                justifyContent = JustifyContent.spaceBetween
                padding = 16.px
                backgroundColor = NamedColor.lightblue
            }
            NavLink {
                css {
                    margin = 8.px
                    padding = 8.px
                    color = NamedColor.white
                    textDecoration = None.none
                    hover {
                        backgroundColor = NamedColor.darkblue
                    }
                }
                to = "/"
                +"Root"
            }
            listOf("about", "fail").forEach { title ->
                NavLink {
                    css {
                        margin = 8.px
                        padding = 8.px
                        color = NamedColor.white
                        textDecoration = None.none
                        hover {
                            backgroundColor = NamedColor.darkblue
                        }
                    }
                    to = "/$title"
                    +title.replaceFirstChar { it.titlecase() }
                }
            }
        }
    }

val RootContent =
    FC {
        NavBar()
        div {
            +"Root"
        }
    }
val AboutContent =
    FC {
        NavBar()
        div {
            +"About page"
        }
    }
val FailContent =
    FC {
        NavBar()
        // エラーが発生するようにしてみる
        throw Error("Failed to render")
    }

val ErrorPage =
    FC<Props> {
        val error = useRouteError().unsafeCast<JsError>()

        div {
            css {
                color = NamedColor.red
            }
            +error.message
        }
    }

fun main() {
    try {
        val container = document.getElementById("root") ?: error("Couldn't find root container!")
        createRoot(container).render(
            Fragment.create {
                RouterProvider {
                    router =
                        createHashRouter(
                            arrayOf(
                                RouteObject(
                                    path = "/",
                                    Component = RootContent,
                                ),
                                RouteObject(
                                    path = "/about",
                                    Component = AboutContent,
                                ),
                                RouteObject(
                                    path = "/fail",
                                    Component = FailContent,
                                    ErrorBoundary = ErrorPage,
                                ),
                            ),
                        )
                }
            },
        )
    } catch (e: Exception) {
        println(e.message ?: "An error occurred")
        alert(e.message ?: "An error occurred")
    }
}

通常の場合と異なり、NavLink を使ってリンクを作成します。

jso を使ってるところがちょっと生々しいですが、react-router の機能をそのまま使えているので、機能的には十分だと思います。

エラーの処理

デフォルトではエラーページは以下のようになります。

img.png

以下のように自分で、エラーページを作成することもできます。

kotlin
val ErrorPage = FC<Props> {
    val error = useRouteError().unsafeCast<JsError>()

    div {
        css {
            color = NamedColor.red
        }
        +error.message
    }
}

以下のように、ErrorBoundary を設定するとエラー処理が出来ます。

kotlin
RouteObject(
    path = "/fail",
    Component = FailContent,
    ErrorBoundary = ErrorPage,
)