Универсальный контейнер. Свойства

external interface RestContainerChildProps<E> : Props {
    var items: Array<Item<E>>
    var addElement: (E) -> Unit
    var updateItem: (Item<E>) -> Unit
    var deleteItem: (ItemId) -> Unit
}

Универсальный контейнер. Функция создания

inline fun <reified E : Any> restContainer(
    url: String,
    child: FC<RestContainerChildProps<E>>,
    queryId: String,
    displayName: String = "RestContainer"
) = FC(displayName) { _: Props ->

Универсальный контейнер. Запрос

val queryClient = useQueryClient()
val myQueryKey = arrayOf(queryId).unsafeCast<QueryKey>()

val query = useQuery<String, QueryError, String, QueryKey>(
    queryKey = myQueryKey,
    queryFn = {
        fetchText(url)
    }
)

Универсальный контейнер. Дочерний компонент

invalidateRepoKey.Provider(myQueryKey) {
    child {
        items = Json.decodeFromString(query.data ?: "")
        addElement = {
            addMutation.mutateAsync(it, null)
        }
        updateItem = {
            updateMutation.mutateAsync(it, null)
        }
        deleteItem = {
            deleteMutation.mutateAsync(it, null)
        }
    }
}

Универсальный список. Функция

inline fun <reified E : Any> restList(
    cElementInList: FC<ElementInListProps<E>>,
    cAddItem: FC<EditAddProps<E>>,
    cEditItem: FC<EditItemProps<E>>,
    displayName: String = "ListContainer"
) = FC(displayName) { props: RestContainerChildProps<E> ->

Универсальный список. Свойства дочерних компонентов

external interface ElementInListProps<E> : Props {
    var element: E
}

external interface EditAddProps<E> : Props {
    var saveElement: (E) -> Unit
}

external interface EditItemProps<E> : Props {
    var item: Item<E>
    var saveElement: (E) -> Unit
}

Универсальный список. Действия с элементами

var editedIndex by useState(-1)
    cAddItem {
        saveElement = { props.addElement(it) }
    }
    val editedItem = props.items.getOrNull(editedIndex)
    if (editedItem != null)
        cEditItem {
            item = editedItem
            saveElement = { 
                props.updateItem(Item(it, editedItem.id)) 
            }
            key = editedItem.id
        }

Пример дочернего компонента

val CLessonAdd = FC<EditAddProps<Lesson>>("LessonNew") { props ->
    var name by useState("")
    div {
        input {
            type = InputType.text
            value = name
            onChange = { name = it.target.value }
        }
        button {
            +"✔"
            onClick = {
                props.saveElement(Lesson(name))
            } } } }

Использование списка с контейнером

Route {
    path = "lessons"
    val list: FC<RestContainerChildProps<Lesson>> =
        restList(
            CLessonInList,
            CLessonAdd,
            CLessonEditContainer
        )
    element = restContainer(
        Config.lessonsPath,
        list,
        "lessons"
    ).create()
}

Дополнительные операции

val CAddStudentToLesson = FC<AddStudentProps>("AddStudent")
{ props ->
    val queryClient = useQueryClient()
    val invalidateRepoKey = useContext(invalidateRepoKey)
    val addStudentMutation = 
        useMutation<HTTPResult, Any, StudentId, Any>(
        mutationFn = { studentId ->
            fetch(
"${Config.lessonsPath}/${props.lesson.id}/students/$studentId",
            )},
            options = jso {
                onSuccess = { _: Any, _: Any, _: Any? ->
queryClient.invalidateQueries<Any>(invalidateRepoKey)
            }})