Building a Pokedex app with Kotlin, KVision and Redux (part 1)
Tags: kvision, kotlin, redux, webdev
Published: 2019-03-26 11:35:43

---
title: Building a Pokedex app with Kotlin, KVision and Redux (part 1)
published: true
description: This guide shows how to build a web application with KVision and Redux.
tags: kvision,kotlin,redux,webdev
---

Hello all :)

In this article I will show you how to build a web application with [Kotlin](https://kotlinlang.org/), [KVision](https://github.com/rjaros/kvision) and [Redux](https://redux.js.org/). You could know Redux quite well, but you certainly do not know how easy it can be used in a web application created in Kotlin.

KVision is an open source web framework created for Kotlin/JS. It allows developers to build modern web applications with the Kotlin language. You can find my short introduction to KVision in [this article](https://dev.to/rjaros/an-introduction-to-kvision-39i1).

Redux is a popular predictable state container for JavaScript apps. Support for Redux, in the form of `kvision-redux` module, was added to KVision just a week ago.

In the first part of the article we will design and implement the Redux model for our application. We will implement the state class, the actions classes and the reducer function. In the second part we will create the user interface.

You can find [the complete application](https://rjaros.github.io/kvision-examples/pokedex/) and all the sources in the [kvision-examples](https://github.com/rjaros/kvision-examples) GitHub repository.

# The specification

We want to create a simple application with the following features:

- It shows a list of Pokémons with a name and a picture.
- It uses [PokéAPI](https://pokeapi.co/) RESTful services.
- It allows to search for a Pokémon by name.
- It displays the results in the responsive grid with a pagination.

This application is loosely based on [this project](https://github.com/alik0211/pokedex), from the official React examples.  

# The template

To start we will clone the `template` project from the [kvision-examples](https://github.com/rjaros/kvision-examples) GitHub repository. To work with Redux we need to add `kvision-redux` module. These are the dependencies in our `build.gradle` file:

```groovy
dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-js:${kotlinVersion}"
    compile "pl.treksoft:kvision:${kvisionVersion}"
    compile "pl.treksoft:kvision-bootstrap:${kvisionVersion}"
    compile "pl.treksoft:kvision-i18n:${kvisionVersion}"
    compile "pl.treksoft:kvision-redux:${kvisionVersion}"
}
```

# The state

The state of the application will be stored in the `Pokedex` class. It contains the information about the downloading process (which can possibly end with an error), the list of all downloaded Pokémon objects, a list of Pokémons visible on the current page, the current search string, the current page number and the number of available pages. KVision requires the state object to be serializable with [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) library, so we need a `@Serializable` annotations on the `Pokedex` and the `Pokemon` classes.

```kotlin
@Serializable
data class Pokemon(val name: String, val url: String)

@Serializable
data class Pokedex(
    val downloading: Boolean,
    val errorMessage: String?,
    val pokemons: List<Pokemon>,
    val visiblePokemons: List<Pokemon>,
    val searchString: String?,
    val pageNumber: Int,
    val numberOfPages: Int
)
```

# The actions

The actions in Redux correspond to events that can change the state of the application. We will implement the actions as sealed class hierarchy inherited from `redux.RAction` class.

```kotlin
sealed class PokeAction : RAction {
    object StartDownload : PokeAction()
    object DownloadOk : PokeAction()
    data class DownloadError(val errorMessage: String) : PokeAction()
    data class SetPokemonList(val pokemons: List<Pokemon>) : PokeAction()
    data class SetSearchString(val searchString: String?) : PokeAction()
    object NextPage : PokeAction()
    object PrevPage : PokeAction()
}
```
The actions with parameters are implemented as data classes and the others are just simple objects.

# The reducer function

The reducer function describes how an action transforms the current state into the new state. It's the main place for the application logic.

To reuse some code we will create two helper extension functions. The first one filters the list of `Pokemon` objects with the given search string and returns original list if the string is `null`.

```kotlin
fun List<Pokemon>.filterBySearchString(searchString: String?): List<Pokemon> {
    return searchString?.let { search ->
        this.filter {
            it.name.toLowerCase().contains(search.toLowerCase())
        }
    } ?: this
}
```
The second function cuts a sublist from the original list based on the given page number. It uses the `MAX_ON_PAGE` constant, which is defined as 12, and do some very simple math.

```kotlin
fun List<Pokemon>.subListByPageNumber(pageNumber: Int): List<Pokemon> {
    return this.subList((pageNumber) * MAX_ON_PAGE, (pageNumber + 1) * MAX_ON_PAGE)
}
```
Writing the reducer function is now quite easy. The first four actions just change the state properties. Other actions are a bit more complicated - they calculate the new list of visible Pokémons and we use the helper functions defined above for that purpose. We can implement the whole `pokedexReducer` function as follows:

```kotlin
fun pokedexReducer(state: Pokedex, action: PokeAction): Pokedex = when (action) {
    is PokeAction.StartDownload -> state.copy(downloading = true)
    is PokeAction.DownloadOk -> state.copy(downloading = false)
    is PokeAction.DownloadError -> state.copy(downloading = false, errorMessage = action.errorMessage)
    is PokeAction.SetPokemonList -> state.copy(pokemons = action.pokemons)
    is PokeAction.SetSearchString -> {
        val filteredPokemon = state.pokemons.filterBySearchString(action.searchString)
        val visiblePokemons = filteredPokemon.take(MAX_ON_PAGE)
        state.copy(
            visiblePokemons = visiblePokemons,
            searchString = action.searchString,
            pageNumber = 0,
            numberOfPages = ((filteredPokemon.size - 1) / MAX_ON_PAGE) + 1
        )
    }
    is PokeAction.NextPage -> if (state.pageNumber < state.numberOfPages - 1) {
        val newPageNumber = state.pageNumber + 1
        val visiblePokemons = state.pokemons.filterBySearchString(state.searchString).subListByPageNumber(newPageNumber)
        state.copy(visiblePokemons = visiblePokemons, pageNumber = newPageNumber)
    } else {
        state
    }
    is PokeAction.PrevPage -> if (state.pageNumber > 0) {
        val newPageNumber = state.pageNumber - 1
        val visiblePokemons = state.pokemons.filterBySearchString(state.searchString).subListByPageNumber(newPageNumber)
        state.copy(visiblePokemons = visiblePokemons, pageNumber = newPageNumber)
    } else {
        state
    }
}
```

# The store

Our data model is now almost complete. All that's left is to create the Redux store with some initial state. We use `createReduxStore` function for this.

```kotlin
val store = createReduxStore(::pokedexReducer, Pokedex(false, null, listOf(), listOf(), null, 0, 1))
```

We can now dispatch actions (or action creator functions) to the store. When the application starts, we have to download all Pokémons data from the PokéAPI servers. We call `dispatch` in the following way.

```kotlin
    store.dispatch(downloadPokemons())

    // ...

    private fun downloadPokemons(): ActionCreator<dynamic, Pokedex> {
        return { dispatch, _ ->
            val restClient = RestClient()
            dispatch(PokeAction.StartDownload)
            restClient.remoteCall(
                "https://pokeapi.co/api/v2/pokemon/", obj { limit = 800 },
                deserializer = Pokemon.serializer().list
            ) {
                it.results
            }.then { list ->
                dispatch(PokeAction.DownloadOk)
                dispatch(PokeAction.SetPokemonList(list))
                dispatch(PokeAction.SetSearchString(null))
            }.catch { e ->
                val info = if (!e.message.isNullOrBlank()) { " (${e.message})" } else { "" }
                dispatch(PokeAction.DownloadError("Service error!$info"))
            }
        }
    }
```
The `downloadPokemons` method returns ActionCreator function, which uses `RestClient` instance to call PokéAPI endpoints, receives the data and finally dispatches `SetPokemonList` and `SetSearchString` actions. It also handles the possible error in the downloading process.

Other actions are dispatched from the user interface elements, but we will take care of the GUI in the next part of this article. I will post it as soon as it's ready.

For now any feedback is highly appreciated.

Cheers :)

[Part 2](https://dev.to/rjaros/building-a-pokedex-app-with-kotlin-kvision-and-redux-part-2-59fi)