Skip to content
This repository was archived by the owner on May 17, 2023. It is now read-only.

Commit 5fed0f7

Browse files
Fix IR forwards-compatibility for example project.
1 parent e16af5c commit 5fed0f7

8 files changed

+69
-71
lines changed

Building Web Applications with React and Kotlin JS/02_Setting_up.md

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,70 +4,54 @@
44

55
To get started, let's make sure we have installed an up-to-date development environment. All we need to get started is:
66

7-
- IntelliJ IDEA (version `2020.1` or above) with the Kotlin plugin (`1.4.0` or above) – [Download/Install](https://www.jetbrains.com/idea/download/)
7+
- IntelliJ IDEA (version `2020.3` or above) with the Kotlin plugin (`1.4.30` or above) – [Download/Install](https://www.jetbrains.com/idea/download/)
88

99

1010
### Setting up the project
1111

12-
We are going to set up our project using the `org.jetbrains.kotlin.js` Gradle plugin. This state-of-the-art plugin takes care of managing a development environment for us that uses all the latest and greatest things from the JavaScript ecosystem – under the hood, it equips us with a `yarn` and `webpack` installation. If we need to make adjustments, we can do so through Gradle – and with just a little bit of configuration, it will allow us to accomplish all the tasks we are going to encounter on our learning journey.
12+
For this tutorial, we have made a starter template available that includes all configuration and required dependencies for the project.
1313

14-
The easiest way to get started is through the wizard provided by IntelliJ IDEA. From the splash screen or from the `File` menu, we select `New/Project...`. We choose the `Gradle` category, turn on the `Kotlin DSL build script`, and select only `Kotlin/JS for browser` as our target:
14+
[**Please clone the project repository from GitHub, and open it in IntelliJ IDEA.**](https://github.com/kotlin-hands-on/web-app-react-kotlin-js-gradle)
1515

16-
![](./assets/new_gradle_project.png)
16+
The template repository contains a basic Kotlin/JS Gradle project for us to build our project. Because it already contains all dependencies that we will need throughout the hands-on, **you don't need to make any changes to the Gradle configuration.**
1717

18-
After clicking the `Next` button, we get to give our project a name. I named my project `confexplorer` – because that is what we are building – but feel free to get creative with your naming:
18+
It is still beneficial to understand what artifacts are being used for the application, so let's have a closer look at our project template and the dependencies and configuration it relies on.
1919

20-
![](./assets/confexplorer.png)
21-
22-
After clicking finish, we can lean back for a few seconds as Gradle initialises a blank project for us that supports JavaScript as a Kotlin compilation target. Once the import has finished, it's time to bring in all those dependencies we will require for the rest of the hands-on.
2320

2421
#### Gradle dependencies and tasks
2522

26-
Throughout the hands-on, we will make use of React, some external dependencies, and even some Kotlin-specific libraries. To save ourselves from running Gradle imports after each chapter, we will add all dependencies right now. The topics related to each set of dependencies is described in the annotated chapter.
27-
28-
Inside our `build.gradle.kts` file, let's make sure that our `repositories` block looks as follows:
29-
30-
```kotlin
31-
repositories {
32-
maven("https://kotlin.bintray.com/kotlin-js-wrappers/")
33-
mavenCentral()
34-
jcenter()
35-
}
36-
```
23+
Throughout the hands-on, we will make use of React, some external dependencies, and even some Kotlin-specific libraries. The topics related to each set of dependencies is described in the annotated chapter.
3724

38-
Now that we have all sources for our dependencies, let's make sure we include everything we need in our `dependencies` block.
25+
Our buildfile's `dependencies` block contains everything we'll need:
3926

4027
```kotlin
4128
dependencies {
42-
implementation(kotlin("stdlib-js"))
4329

4430
//React, React DOM + Wrappers (chapter 3)
45-
implementation("org.jetbrains:kotlin-react:16.13.1-pre.110-kotlin-1.4.0")
46-
implementation("org.jetbrains:kotlin-react-dom:16.13.1-pre.110-kotlin-1.4.0")
47-
implementation(npm("react", "16.13.1"))
48-
implementation(npm("react-dom", "16.13.1"))
31+
implementation("org.jetbrains:kotlin-react:17.0.1-pre.148-kotlin-1.4.21")
32+
implementation("org.jetbrains:kotlin-react-dom:17.0.1-pre.148-kotlin-1.4.21")
33+
implementation(npm("react", "17.0.1"))
34+
implementation(npm("react-dom", "17.0.1"))
4935

5036
//Kotlin Styled (chapter 3)
51-
implementation("org.jetbrains:kotlin-styled:1.0.0-pre.110-kotlin-1.4.0")
52-
implementation(npm("styled-components", "~5.1.1"))
53-
implementation(npm("inline-style-prefixer", "~6.0.0"))
37+
implementation("org.jetbrains:kotlin-styled:5.2.1-pre.148-kotlin-1.4.21")
38+
implementation(npm("styled-components", "~5.2.1"))
5439

5540
//Video Player (chapter 7)
56-
implementation(npm("react-player", "~2.6.0"))
41+
implementation(npm("react-youtube-lite", "1.0.1"))
5742

5843
//Share Buttons (chapter 7)
5944
implementation(npm("react-share", "~4.2.1"))
6045

6146
//Coroutines (chapter 8)
62-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")
47+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3")
6348
}
6449
```
6550

66-
After editing the file, IntelliJ IDEA will automatically prompt us to import the changed Gradle files. Alternatively, we can also press the "🔁 Reimport All Gradle Projects" button in the Gradle tool window.
6751

6852
#### HTML page
6953

70-
Because we can't run JavaScript out of nowhere, we need to provide an HTML page (linked to our compiled JS file) that can be loaded in a browser. Let's create the file `/src/main/resources/index.html` and fill it with the following content:
54+
Because we can't run JavaScript out of nowhere, we also need an HTML page to insert out HTML into. The file `/src/main/resources/index.html` is provided and filled accordingly:
7155

7256
```xml
7357
<!doctype html>
@@ -83,9 +67,9 @@ Because we can't run JavaScript out of nowhere, we need to provide an HTML page
8367
</html>
8468
```
8569

86-
Depending on how we named our project, the embedded `js` file has a different name. For example, if you named your project `followingAlong`, make sure to embed `followingAlong.js`. Thanks to the Gradle plugin, all of our code and dependencies will be bundled up into this single JavaScript artifact that bears the same name as our project.
70+
Depending on how we named our project, the embedded `js` file has a different name. So, if you're working in a project named `followingAlong`, make sure to embed `followingAlong.js`. Thanks to the Gradle plugin, all of our code and dependencies will be bundled up into this single JavaScript artifact that bears the same name as our project.
8771

88-
Now, before we write a proper "Hello, World" with actual Markup, let's start with a very simple and visual example – a solid colored page. This is just to verify that what we're building is actually reaching the browser and executes fine. For this, we create the file `src/main/kotlin/Main.kt` and fill it with the following Kotlin code snippet:
72+
Now, before we write a proper "Hello, World" with actual markup, we start with a very simple and visual example – a solid colored page. This is just to verify that what we're building is actually reaching the browser and executes fine. For this, we have the file `src/main/kotlin/Main.kt`, filled with the following code snippet:
8973

9074
```kotlin
9175
import kotlinx.browser.document

Building Web Applications with React and Kotlin JS/03_A_First_Static_Page.md

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,23 +102,35 @@ Type or paste the above code as the contents of your `render` call. If IntelliJ
102102

103103
While writing HTML in Kotlin just for the sake of it is a noble idea, there are actually a lot more benefits to writing your HTML directly inside Kotlin. A big advantage of using this domain specific language is that we can manipulate our website content using language constructs we are already familiar with. Whether it's conditions, loops, collections, or string interpolation, we can expect them to work the same in HTML as they would in Kotlin.
104104

105-
Now, instead of hardcoding the list of videos, let's actually define them as a list of Kotlin objects and display those objects instead. We'll create a simple class to hold together the attributes of a video (we can do this in `Main.kt` or a file of our choice):
105+
Now, instead of hardcoding the list of videos, let's actually define them as a list of Kotlin objects and display those objects instead. We'll create a simple data class to hold together the attributes of a video called `KotlinVideo` (we can do this in `Main.kt` or a file of our choice). We will also define a corresponding `external interface` – more on that later.
106106

107107
```kotlin
108-
data class Video(val id: Int, val title: String, val speaker: String, val videoUrl: String)
108+
external interface Video {
109+
val id: Int
110+
val title: String
111+
val speaker: String
112+
val videoUrl: String
113+
}
114+
115+
data class KotlinVideo(
116+
override val id: Int,
117+
override val title: String,
118+
override val speaker: String,
119+
override val videoUrl: String
120+
) : Video
109121
```
110122

111123
Then, let's fill up the two lists for unwatched videos and watched videos respectively. For now, we can just have these declarations at file-level inside our `Main.kt`:
112124

113125
```kotlin
114126
val unwatchedVideos = listOf(
115-
Video(1, "Building and breaking things", "John Doe", "https://youtu.be/PsaFVLr8t4E"),
116-
Video(2, "The development process", "Jane Smith", "https://youtu.be/PsaFVLr8t4E"),
117-
Video(3, "The Web 7.0", "Matt Miller", "https://youtu.be/PsaFVLr8t4E")
127+
KotlinVideo(1, "Building and breaking things", "John Doe", "https://youtu.be/PsaFVLr8t4E"),
128+
KotlinVideo(2, "The development process", "Jane Smith", "https://youtu.be/PsaFVLr8t4E"),
129+
KotlinVideo(3, "The Web 7.0", "Matt Miller", "https://youtu.be/PsaFVLr8t4E")
118130
)
119131

120132
val watchedVideos = listOf(
121-
Video(4, "Mouseless development", "Tom Jerry", "https://youtu.be/PsaFVLr8t4E")
133+
KotlinVideo(4, "Mouseless development", "Tom Jerry", "https://youtu.be/PsaFVLr8t4E")
122134
)
123135
```
124136

@@ -146,9 +158,8 @@ We do not need to do perform any extra steps to start using the functionality, b
146158
dependencies {
147159
//...
148160
//Kotlin Styled (chapter 3)
149-
implementation("org.jetbrains:kotlin-styled:1.0.0-pre.110-kotlin-1.4.0")
150-
implementation(npm("styled-components", "~5.1.1"))
151-
implementation(npm("inline-style-prefixer", "~6.0.0"))
161+
implementation("org.jetbrains:kotlin-styled:5.2.1-pre.148-kotlin-1.4.21")
162+
implementation(npm("styled-components", "~5.2.1"))
152163
//...
153164
}
154165
```

Building Web Applications with React and Kotlin JS/04_Making_It_React.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Let's begin the process of structuring our application. We’ll start by explici
1717
```kotlin
1818
import react.*
1919

20+
@JsExport
2021
class App : RComponent<RProps, RState>() {
2122
override fun RBuilder.render() {
2223
// typesafe HTML goes here!
@@ -46,6 +47,7 @@ Let's create a new file called `VideoList.kt`. The `VideoList` class follows the
4647
import react.*
4748
import react.dom.*
4849

50+
@JsExport
4951
class VideoList: RComponent<RProps, RState>() {
5052
override fun RBuilder.render() {
5153
for (video in unwatchedVideos) {
@@ -90,6 +92,7 @@ external interface VideoListProps: RProps {
9092
We now adjust the class definition of `VideoList` to make use of those props:
9193

9294
```kotlin
95+
@JsExport
9396
class VideoList: RComponent<VideoListProps, RState>() {
9497
override fun RBuilder.render() {
9598
for (video in props.videos) {
@@ -186,6 +189,7 @@ There are a few things we need to do to add this state:
186189

187190

188191
```kotlin
192+
@JsExport
189193
class VideoList : RComponent<VideoListProps, VideoListState>() {
190194
override fun RBuilder.render() {
191195
for (video in props.videos) {

Building Web Applications with React and Kotlin JS/05_Working_Together_Composing_Components.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ external interface AppState : RState {
2121
And we make sure to use this interface in the `App` class:
2222

2323
```kotlin
24+
@JsExport
2425
class App : RComponent<RProps, AppState>()
2526
```
2627

2728
Let's delete the `VideoListState` since we're storing it somewhere further up our hierarchy now. Since we've made the list effectively stateless at this point (by moving all of the state out of the component), we can revert its class definition back to inherit the default state.
2829

2930
```kotlin
31+
@JsExport
3032
class VideoList : RComponent<VideoListProps, RState>()
3133
```
3234

Building Web Applications with React and Kotlin JS/06_More_Components.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ external interface VideoPlayerProps : RProps {
1717
var video: Video
1818
}
1919

20+
@JsExport
2021
class VideoPlayer : RComponent<VideoPlayerProps, RState>() {
2122
override fun RBuilder.render() {
2223
styledDiv {

Building Web Applications with React and Kotlin JS/07_Using_Packages_From_NPM.md

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,51 @@ First up and probably the most obvious missing functionality is the video player
66

77
### Adding the video player component
88

9-
We need to replace our placeholder video component with one that can actually show the YouTube videos we're linking by adding in a ready-made video player for React. We'll use the `react-player` component to show the video and control the appearance of the player. We can take a look at some of the documentation for it and its API in the GitHub [README](https://github.com/CookPete/react-player/blob/master/README.md).
9+
We need to replace our placeholder video component with one that can actually show the YouTube videos we're linking by adding in a ready-made video player for React. We'll use the `react-youtube-lite` component to show the video and control the appearance of the player. We can take a look at some of the documentation for it and its API in the GitHub [README](https://www.npmjs.com/package/react-youtube-lite).
1010

11-
In the beginning, we have already added the `react-player` package to our Gradle build file. This was the responsible snippet:
11+
In the beginning, we have already added the `react-youtube-lite` package to our Gradle build file. This was the responsible snippet:
1212

1313
```kotlin
1414
dependencies {
1515
//...
1616
//Video Player (chapter 7)
17-
implementation(npm("react-player", "~2.6.0"))
17+
implementation(npm("react-youtube-lite", "1.0.1"))
1818
//...
1919
}
2020
```
2121

2222
You're seeing that right – NPM dependencies can be added to a Gradle build file via the `npm` function. The yarn installation managed by the Gradle plugin will take care of downloading, installing and updating those NPM dependencies for you.
2323

24-
Since we want to use this module from Kotlin, we need to tell our compiler what the component interface looks like – what kind of things are okay to invoke, to set, or to read from this external component, so that we remain safe, and can count on tool support. To do this, let's create a file called `ReactPlayer.kt`, with the following contents:
24+
Since we want to use this module from Kotlin, we need to tell our compiler what the component interface looks like – what kind of things are okay to invoke, to set, or to read from this external component, so that we remain safe, and can count on tool support. To do this, let's create a file called `ReactYouTube.kt`, with the following contents:
2525

2626
```kotlin
27-
@file:JsModule("react-player")
27+
@file:JsModule("react-youtube-lite")
2828
@file:JsNonModule
2929

3030
import react.*
3131

32-
@JsName("default")
32+
@JsName("ReactYouTubeLite")
3333
external val reactPlayer: RClass<dynamic>
3434
```
3535

36-
Because JavaScript imports/exports isn't the simplest topic, it can sometimes be tricky to find the correct combination between annotations to get the Kotlin compiler on the same page as us. These last two lines are equivalent to a JavaScript import like `require("react-player").default;`. It tells the compiler that we're certain we'll get a component conforming to `RClass<dynamic>` at runtime.
36+
Because JavaScript imports/exports isn't the simplest topic, it can sometimes be tricky to find the correct combination between annotations to get the Kotlin compiler on the same page as us. These last two lines are equivalent to a JavaScript import like `require("react-youtube-lite").default;`. It tells the compiler that we're certain we'll get a component conforming to `RClass<dynamic>` at runtime.
3737

3838
#### Typed wrappers for the video player component
3939

4040
However, in this configuration, we're giving up a lot of the benefits that Kotlin gives us. The declaration of `dynamic` essentially tells the compiler to just accept whatever we give it, at the risk of breaking things at runtime (also commonly known as *in production*).
4141

42-
Fortunately, we know the structure of the interfaces used by the imported components (or can infer them rather quickly from the [README](https://github.com/CookPete/react-player#usage)), so making our wrappers typesafe is a rather straightforward task. We can define a typesafe external interface which allows us to set the URL as we see fit. And we modify the `ReactPlayer` definition accordingly:
42+
Fortunately, we know the structure of the interfaces used by the imported components (or can infer them rather quickly from the [README](https://www.npmjs.com/package/react-youtube-lite)), so making our wrappers typesafe is a rather straightforward task. We can define a typesafe external interface which allows us to set the URL as we see fit. And we modify the `ReactPlayer` definition accordingly:
4343

4444
```kotlin
45-
//...
46-
external val reactPlayer: RClass<ReactPlayerProps>
45+
@file:JsModule("react-youtube-lite")
46+
@file:JsNonModule
47+
48+
import react.*
49+
50+
@JsName("ReactYouTubeLite")
51+
external val reactPlayer: RClass<ReactYouTubeProps>
4752

48-
external interface ReactPlayerProps : RProps {
53+
external interface ReactYouTubeProps : RProps {
4954
var url: String
5055
}
5156
```

Building Web Applications with React and Kotlin JS/08_Using_an_External_REST_API.md

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,12 @@ Inside `App.kt` (or a new file), let's write a method that can `fetch` a video f
4343

4444
```kotlin
4545
suspend fun fetchVideo(id: Int): Video {
46-
val responsePromise = window.fetch("https://my-json-server.typicode.com/kotlin-hands-on/kotlinconf-json/videos/$id")
47-
val response = responsePromise.await()
48-
val jsonPromise = response.json()
49-
val json = jsonPromise.await()
50-
return json.unsafeCast<Video>()
46+
val response = window
47+
.fetch("https://my-json-server.typicode.com/kotlin-hands-on/kotlinconf-json/videos/$id")
48+
.await()
49+
.json()
50+
.await()
51+
return response as Video
5152
}
5253
```
5354

@@ -62,16 +63,6 @@ Let's look at what's happening in this *suspending function*. We `fetch` a video
6263

6364
A function call like `window.fetch` returns a `Promise` object. We would have to define a callback handler which gets invoked once the `Promise` is *resolved* and a result is available. However, since we are using coroutines in our project, we can `await` those promises. We're writing code that looks sequential but remains non-blocking. Whenever a function like `await()` is called, the method stops its execution (it *suspends*, hence the keyword). It continues execution once the `Promise` can be resolved.
6465

65-
The individual variables were only used for illustration purposes, though. In reality, we can, of course, chain all calls together. We'll end up with a single expression, so we can even use the expression body syntax to express the same processing steps as above:
66-
67-
```kotlin
68-
suspend fun fetchVideo(id: Int): Video =
69-
window.fetch("https://my-json-server.typicode.com/kotlin-hands-on/kotlinconf-json/videos/$id")
70-
.await()
71-
.json()
72-
.await()
73-
.unsafeCast<Video>()
74-
```
7566

7667
#### Fanning out
7768

Building Web Applications with React and Kotlin JS/09_Deploying_to_Production.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ heroku buildpacks:set heroku/gradle
2828
heroku buildpacks:add https://github.com/heroku/heroku-buildpack-static.git
2929
```
3030

31-
To allow the `heroku/gradle` buildpack to run properly, a `stage` task needs to be present in our Gradle build file. Luckily, it is equivalent to our `build` task, so the changes we have to make at the bottom of our `build.gradle.kts` file are very limited:
31+
To allow the `heroku/gradle` buildpack to run properly, a `stage` task needs to be present in our Gradle build file. Luckily, it is equivalent to our `build` task – and, as luck would have it, the corresponding alias is already included at the bottom of our Gradle build file:
3232

3333
```kotlin
3434
// Heroku Deployment (chapter 9)
@@ -61,4 +61,4 @@ If everything has gone according to plan, we will see the URL under which we can
6161

6262
![image-20190730200111014](./assets/deployingToProduction.png)
6363

64-
You can find the state of the project after this section on the `step-08-deploying-to-production` branch in the [GitHub](https://github.com/kotlin-hands-on/web-app-react-kotlin-js-gradle/tree/step-08-deploying-to-production) repository.
64+
You can find the state of the project after this section on the `final` branch in the [GitHub](https://github.com/kotlin-hands-on/web-app-react-kotlin-js-gradle/tree/final) repository.

0 commit comments

Comments
 (0)