Что происходит, когда Котлин влюбляется в React

React.js Kotlin
Что происходит, когда Котлин влюбляется в React

Говоря о Kotlin, первая реакция большинства людей, которые о нем слышали, заключается в том, что это язык для разработки под Android. Нужно сказать, что Google продвигает Kotlin намного больше, чем компания-основатель Kotlin, Jetbrains.

Kotlin может писать не только Android, но и сервер, можно сказать, что пока вы можете писать Java, вы можете использовать Kotlin для его замены. Конечно, Kotlin — это гораздо больше. В настоящее время Kotlin может разрабатывать и использовать следующие платформы. jvm, android, js и родной (бета). Из-за возможности разработки Native Kotlin теперь поддерживает разработку ios, хотя кажется, что эффект не так хорош.

Разработка React с Kotlin сегодня — это всего лишь пробная версия KotlinJs.

окружающая обстановка

  • Браузер: Microsoft Edge 77.0.235.5 (официальная сборка) для разработчиков (64-разрядная версия)
  • Kotlin: 1.3.12

Среда установки

Давным-давно на официальном сайте kotlin появились две библиотеки, эти две библиотеки в основном для улучшения экологии реакции в kotlin.JetBrains/create-react-kotlin-app, каркас для генерации проектов React; второй —JetBrains/kotlin-wrappers, в котором хранятся экологические компоненты вокруг реакции, такие как маршрутизатор, редукс и т. д.

Согласно документации скаффолдинга, одна строка кода может сгенерировать наш проект.

npx create-react-kotlin-app my-app

После создания проекта откройте проект и используйтеnpm startилиyarn startСтартовый проект.

Проект автоматически откроет браузер, а затем увидит наш проект, что означает, что наш проект запущен.

Мы можем посмотреть, что есть в официальной библиотеке оберток, кажется, что во всем отсутствует зрелая библиотека компонентов пользовательского интерфейса. Означает ли это, что мы должны сами писать все стили?

конечно, нет.

kotlinjs может вызывать модули nodejs, но это немного раздражает.

установить муравей

ant — это набор компонентов пользовательского интерфейса, разработанный Ali front-end, который имеет версию React. Сначала установите библиотеку.

пройти черезyarn add antdУстановите и дождитесь завершения установки.

После окончания установки импортируем ant demo в наш проект, открываем index.css и импортируем проект.

@import '~antd/dist/antd.css';

Давайте попробуем ввести кнопку, чтобы увидеть.

Затем мы видим, что на странице это по-прежнему оригинальная кнопка, которая не имеет ничего общего со стилем ant. Это означает, что мы еще не говорили об использовании компонентов ant в нашем проекте.

Привязать элементы управления пользовательского интерфейса

Создать новый пакетui/antчтобы сохранить наш класс привязки с компонентом ant.

Как написать наш класс привязки? Это первая трудность, с которой я столкнулся.

Во-первых, найдите каталог antd в наших node_modules и найдите управляющий каталог, который мы хотим связать.

мы используем@file:JsModule("antd/lib/button")для указания каталога привязки. пройти через@JsName("default")Для привязки в это время элемент управления должен бытьexport default Button;

@file:JsModule("antd/lib/button")

package ui.ant

import react.RClass
import react.RProps

@JsName("default")
external val button: RClass<RProps>

Снова введите кнопку, которую мы только что написали, в наш Main.kt. Кнопка на данный момент отличается от react.dom.button , объявленная нами кнопка не может напрямую устанавливать какие-либо свойства, методы и т.д. Конечно, мы ему это устроим.

package app

import react.RBuilder
import react.RComponent
import react.RProps
import react.RState
import react.dom.div

class App : RComponent<RProps, RState>() {
    override fun RBuilder.render() {
        div {
            ui.ant.button {
                +"Button"
            }
        }
    }
}

fun RBuilder.app() = child(App::class) {}

Дождитесь завершения автоматической компиляции и обновления, загляните на нашу страницу.

Наконец-то увидели нашу кнопку, и стиль соответствует официальной документации Ant.

Это означает, что наша привязка теперь успешна.

Затем выполните те же шаги и привяжите другие элементы управления, которые будут использоваться таким же образом.

Привязываем ввод и поиск.

Эта привязка изменилась с прошлого раза, мы можем установить некоторые значения для этих двух свойств, чтобы мы могли использовать их непосредственно в HTML DSL. Здесь мы реализуемRPropsдля повторной настройки свойств, которые мы хотим использовать, правильноInputустановитьplaceholder,а такжеSeacherRPorpsможет быть напрямую унаследованInputRPropsдля расширения свойств.

@file:JsModule("antd/lib/input/Input")

package ui.antd

import react.*

external interface InputRProps:RProps {
    var placeholder:String
}

@JsName("default")
external val input: RClass<InputProps>

Привязать элемент управления поиском

@file:JsModule("antd/lib/input/Search")
 
package ui.antd

import org.w3c.dom.events.Event

import react.*

external interface SearchProps : InputRProps {
    var onChange: (Event) -> Unit
    var value: String
}

@JsName("default")
external val search: RClass<SearchProps>

Создайте новый независимый элемент управления

Мы снова выделяем компонент, называемыйHome.kt. Вставьте нашу кнопку и используйте Home.kt вместо App.kt.

package home

import react.RBuilder
import react.RComponent
import react.RProps
import react.RState
import react.dom.div
import ui.ant.button

class Home : RComponent<RProps, RState>() {
    override fun RBuilder.render() {
        div{
           button{
               +"Button"
           }
        }
    }
}

fun RBuilder.home() = child(Home::class) {}

Ожидание обновления страницы, она все такая же, как и раньше.

Добавить событие клика

Поскольку в нашей кнопке не объявлено событие клика, его нельзя вызвать напрямую, но kotlinjs может вызывать динамические свойства, а через asDynamic мы можем вызывать любые свойства, существующие в html, которые мы хотим использовать.

 button {
	+"Button"
	attrs {
		asDynamic().onClick = {
			console.log("on click")
		}
	}
}

На этом этапе событие нажатия кнопки завершено.Когда мы нажимаем на страницу, мы можем увидеть информацию в консоли браузера для печати.

двусторонняя привязка

Поскольку данные, которые мы хотим использовать, хранятся в RState, мы переписываем их для записи RState. Состояние и свойства — два важных свойства в реакции, состояние используется для изменения и обновления внутренних данных, а свойства — для передачи внешних данных.

Настройте HomeState для наследования RState для изменения внутренних данных.

interface HomeState : RState {
    var inputValue: Int
}

Наш дом в RState типа HomeState

class Home : RComponent<RProps, HomeState>()

Это позволяет использовать данные в состоянии ниже.

Используйте div для записи наших данных.

div {
	+"${state.inputValue}"
}

Незначительные изменения в событии нажатия кнопки и изменения данных в реакции требуют вызова setState.

button {
	+"Button"
	attrs.asDynamic().onClick ={
		setState {
			inputValue += 1
		}
	}
}

Однако после обновления страницы возникает ошибка.TypeError: Cannot read property 'toString' of undefinedМетод toString неопределенного не может быть прочитан.

Получается, что недостаточно объявить только в HomeState, его нужно объявить еще и в HomeStateHomeState.init()Начальное значение назначается в переопределенном методе.

Дождавшись повторного обновления страницы, появился ожидаемый результат. При каждом нажатии кнопки числа складываются.

Работает с Аксиос

Axios — это широко используемый фреймворк HTTP-запросов во внешнем интерфейсе, и официальный представитель kotlin выполнил для него простую инкапсуляцию. Мы можем использовать его прямо здесь.

Прежде всего, все равно сначала установите axios

yarn add axios

После завершения установки создайте новую папку axios, а затем создайте новый Axios.kt.

package axios

import kotlin.js.Promise

@JsModule("axios")
external fun <T> axios(config: AxiosConfigSettings): Promise<AxiosResponse<T>>

// Type definition
external interface AxiosConfigSettings {
    var url: String
    var method: String
    var baseUrl: String
    var timeout: Number
    var data: dynamic
    var transferRequest: dynamic
    var transferResponse: dynamic
    var headers: dynamic
    var params: dynamic
    var withCredentials: Boolean
    var adapter: dynamic
    var auth: dynamic
    var responseType: String
    var xsrfCookieName: String
    var xsrfHeaderName: String
    var onUploadProgress: dynamic
    var onDownloadProgress: dynamic
    var maxContentLength: Number
    var validateStatus: (Number) -> Boolean
    var maxRedirects: Number
    var httpAgent: dynamic
    var httpsAgent: dynamic
    var proxy: dynamic
    var cancelToken: dynamic
}

external interface AxiosResponse<T> {
    val data: T
    val status: Number
    val statusText: String
    val headers: dynamic
    val config: AxiosConfigSettings
}

Делаем простое приложение, введя имя, переходим на Github, чтобы найти соответствующий репозиторий.

Сначала определите наш класс данных для хранения данных, которые мы запрашиваем.

data class Result(
        val total_count: Int,
        val items: Array<Item>
)

data class Item(val id: Long,
                val node_id: String,
                val name: String,
                val full_name: String,
                val html_url: String)

Затем объявите классы, используемые в HomeState.

interface HomeState : RState {
    var inputValue: String
    var repos: Array<Item>
}

Ниже приведен код определения интерфейса.

div(classes = "search-input") {
    search {
        attrs {
            onChange = {
                val element = it.target as HTMLInputElement
                setState {
                    inputValue = element.value
                }
            }
            placeholder = "请输入 Github 仓库名称"
        }
    }
}

button {
    +"搜索"
    attrs {
        asDynamic().onClick = {

        }
    }
}

div(classes = "list") {
    if (state.repos.isNotEmpty()) {
        ul {
            for ((index, item) in state.repos.withIndex()) {
                li {
                    a(href = item.html_url) {
                        +"${index + 1} / ${item.full_name}"
                    }
                }
            }
        }
    }
}

Однако, когда мы предварительно просмотрели интерфейс в это время, мы обнаружили, что он снова и снова ошибался, предполагая, что репозитории не могут быть итерированы.

Очевидно, почему нельзя повторить массив? Оказалось, что при создании компонента Home репозитории не были инициализированы. Здесь вам нужно понять жизненный цикл реакции и инициализировать данные до создания компонента.

    override fun componentWillMount() {
        setState {
            repos = emptyArray()
        }
    }

Теперь страница может отображаться нормально.

Улучшить событие нажатия кнопки.

 button {
    +"搜索"
    attrs {
        asDynamic().onClick = {
            //https://api.github.com/search/repositories?q=
            val config: AxiosConfigSettings = jsObject {
                url = "https://api.github.com/search/repositories?q=${state.inputValue}"
            }
            axios.axios<Result>(config).then { response ->
                setState {
                    repos = response.data.items
                }
            }.catch { error ->
                console.log("error", error)
            }
        }
    }
}

Теперь проект завершен.

Суммировать

В первую очередь заявляю, что автор не профессиональный фронтенд, поэтому надеюсь, что в статье есть ошибки.Более важная цель этой статьи — показать использование и опыт kotlin в других сферах.

Когда kotlin пишет React, его привычки письма должны быть похожи на написание ts, и это дает студентам, работающим с back-end, опыт написания front-end. Однако экология неполная, ни одна библиотека UI-компонентов официально не написана на kt, скорость компиляции низкая, отчет об ошибках иногда не очевиден, информации мало. Однако, если вы хотите стиснуть зубы и развиваться, я лично считаю, что это вполне возможно, но предполагается, что вам придется построить много колес самостоятельно.

Личный публичный аккаунт WeChat