Skip to content

Commit 2dc1d88

Browse files
committed
update: fixes after the 1.7.10 release
1 parent 544a8c6 commit 2dc1d88

File tree

1 file changed

+61
-61
lines changed

1 file changed

+61
-61
lines changed

docs/topics/js/js-react.md

Lines changed: 61 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ This tutorial will teach you how to build a browser application with Kotlin/JS a
44
framework using the Gradle plugin. You will:
55

66
* Complete usual kinds of tasks associated with building a typical React application.
7-
* Explore how [Kotlin's DSLs](https://kotlinlang.org/docs/type-safe-builders.html) can be used to help express concepts
8-
concisely and uniformly without sacrificing readability, allowing to write a fully-fledged application completely in Kotlin.
7+
* Explore how [Kotlin's DSLs](type-safe-builders.md) can be used to help express concepts concisely and uniformly without
8+
sacrificing readability, allowing to write a fully-fledged application completely in Kotlin.
99
* Learn how to use ready-made components created by the community, use external libraries, and publish the final application.
1010

1111
The output will be a website _KotlinConf Explorer_ with an overview of the [KotlinConf](https://kotlinconf.com/) event
@@ -30,24 +30,22 @@ concepts behind React may help understand some sample code but is not strictly r
3030
```kotlin
3131
dependencies {
3232
// React, React DOM + Wrappers
33-
implementation("org.jetbrains.kotlin-wrappers:kotlin-react:17.0.2-pre.297-kotlin-1.6.10")
34-
implementation("org.jetbrains.kotlin-wrappers:kotlin-react-dom:17.0.2-pre.297-kotlin-1.6.10")
35-
implementation(npm("react", "17.0.2"))
36-
implementation(npm("react-dom", "17.0.2"))
33+
implementation(enforcedPlatform("org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom:1.0.0-pre.354"))
34+
implementation("org.jetbrains.kotlin-wrappers:kotlin-react")
35+
implementation("org.jetbrains.kotlin-wrappers:kotlin-react-dom")
3736

38-
// Kotlin Styled
39-
implementation("org.jetbrains.kotlin-wrappers:kotlin-emotion:11.8.2-pre.325-kotlin-1.6.10")
40-
implementation(npm("styled-components", "~5.3.3"))
37+
// Kotlin React Emotion (CSS)
38+
implementation("org.jetbrains.kotlin-wrappers:kotlin-emotion")
4139

4240
// Video Player
43-
implementation(npm("react-lite-youtube-embed", "2.2.2"))
41+
implementation(npm("react-player", "2.10.1"))
4442

4543
// Share Buttons
4644
implementation(npm("react-share", "4.4.0"))
4745

4846
// Coroutines & serialization
49-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
50-
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
47+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3")
48+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3")
5149
}
5250
```
5351

@@ -140,24 +138,24 @@ Replace the code in the `Main.kt` file as follows:
140138
```kotlin
141139
import kotlinx.browser.document
142140
import react.*
143-
import react.css.css
144-
import react.dom.render
141+
import emotion.react.css
145142
import csstype.Position
146143
import csstype.px
147144
import react.dom.html.ReactHTML.h1
148145
import react.dom.html.ReactHTML.h3
149146
import react.dom.html.ReactHTML.div
150147
import react.dom.html.ReactHTML.p
151148
import react.dom.html.ReactHTML.img
149+
import react.dom.client.createRoot
152150
import kotlinx.serialization.Serializable
153151

154152
fun main() {
155153
val container = document.getElementById("root") ?: error("Couldn't find root container!")
156-
render(Fragment.create {
154+
createRoot(container).render(Fragment.create {
157155
h1 {
158156
+"Hello, React+Kotlin/JS!"
159157
}
160-
}, container)
158+
})
161159
}
162160
```
163161
{validate="false"}
@@ -167,7 +165,7 @@ fun main() {
167165
This element is a container defined in `src/main/resources/index.html`, which was included in the template.
168166
* The content is an `<h1>` header and uses a typesafe DSL to render HTML.
169167
* `h1` is a function that takes a lambda parameter. When you add the `+` sign in front of the string literal,
170-
the `unaryPlus()` function is actually invoked using [operator overloading](https://kotlinlang.org/docs/reference/operator-overloading.html).
168+
the `unaryPlus()` function is actually invoked using [operator overloading](operator-overloading.md).
171169
It appends the string to the enclosed HTML element.
172170

173171
When the project recompiles, the browser displays this HTML page:
@@ -177,7 +175,7 @@ When the project recompiles, the browser displays this HTML page:
177175
### Convert HTML to Kotlin
178176

179177
The Kotlin [wrappers](https://github.com/JetBrains/kotlin-wrappers/blob/master/kotlin-react/README.md) for React come
180-
with a [domain-specific language (DSL)](https://kotlinlang.org/docs/type-safe-builders.html) that allows writing HTML in
178+
with a [domain-specific language (DSL)](type-safe-builders.md) that allows writing HTML in
181179
pure Kotlin code. In this way, it's similar to [JSX](https://reactjs.org/docs/introducing-jsx.html) from JavaScript.
182180
However, with this markup being Kotlin, you get all the benefits of a statically typed language, such as autocomplete or type checking.
183181

@@ -305,23 +303,23 @@ sure that the loop is working.
305303

306304
### Add styles with typesafe CSS
307305

308-
The [kotlin-react-css](https://github.com/JetBrains/kotlin-wrappers/tree/master/kotlin-react-css) library allows specifying
309-
CSS attributes – even dynamic ones – right alongside HTML. Conceptually, that makes it similar to
310-
[CSS-in-JS](https://reactjs.org/docs/faq-styling.html#what-is-css-in-js) – but for Kotlin. The benefit of using a DSL is
311-
that you can use Kotlin code constructs to express formatting rules.
306+
The [kotlin-emotion](https://github.com/JetBrains/kotlin-wrappers/blob/master/kotlin-emotion/) wrapper for the [Emotion](https://emotion.sh/docs/introduction)
307+
library allows specifying CSS attributes – even dynamic ones – right alongside HTML with JavaScript. Conceptually, that
308+
makes it similar to [CSS-in-JS](https://reactjs.org/docs/faq-styling.html#what-is-css-in-js) – but for Kotlin.
309+
The benefit of using a DSL is that you can use Kotlin code constructs to express formatting rules.
312310

313-
The template project for this tutorial already includes everything for using `kotlin-react-css`:
311+
The template project for this tutorial already includes everything for using `kotlin-emotion`:
314312

315313
```kotlin
316314
dependencies {
317315
// ...
318-
// Kotlin React CSS
319-
implementation("org.jetbrains.kotlin-wrappers:kotlin-react-css:17.0.2-pre.298-kotlin-1.6.10")
316+
// Kotlin React Emotion (CSS) (chapter 3)
317+
implementation("org.jetbrains.kotlin-wrappers:kotlin-emotion")
320318
// ...
321319
}
322320
```
323321

324-
With `kotlin-react-css`, you can specify a `css` block inside HTML elements `div` and `h3`, where you can define the styles.
322+
With `kotlin-emotion`, you can specify a `css` block inside HTML elements `div` and `h3`, where you can define the styles.
325323

326324
To move the video player to the top right corner of the page, use CSS and adjust the code for the video player
327325
(the last `div` in the snippet):
@@ -404,7 +402,7 @@ Create the main component for rendering into the `root` element, the `App`:
404402
```kotlin
405403
fun main() {
406404
val container = document.getElementById("root") ?: error("Couldn't find root container!")
407-
render(App.create(), container)
405+
createRoot(container).render(App.create())
408406
}
409407
```
410408

@@ -426,6 +424,7 @@ and contains the code from the `unwatchedVideos` list.
426424
import kotlinx.browser.window
427425
import react.*
428426
import react.dom.*
427+
import react.dom.html.ReactHTML.p
429428

430429
val VideoList = FC<Props> {
431430
for (video in unwatchedVideos) {
@@ -436,7 +435,7 @@ and contains the code from the `unwatchedVideos` list.
436435
}
437436
```
438437

439-
2. In `App.kt`, use the `VideoList` component by invoking the component without parameters:
438+
2. In `App.kt`, use the `VideoList` component by invoking it without parameters:
440439

441440
```kotlin
442441
// . . .
@@ -472,8 +471,8 @@ that holds all the props which can be passed to a `VideoList` component:
472471
var videos: List<Video>
473472
}
474473
```
475-
The [external modifier](https://kotlinlang.org/docs/reference/js-interop.html#external-modifier) tells the compiler
476-
that the interface implementation is provided externally, so it doesn't try to generate any JavaScript code from the declaration.
474+
The [external](js-interop.md#external-modifier) modifier tells the compiler that the interface implementation is provided
475+
externally, so it doesn't try to generate any JavaScript code from the declaration.
477476

478477
2. Adjust the class definition of `VideoList` to make use of those props, which are passed into the `FC` block as a parameter:
479478

@@ -531,8 +530,8 @@ p {
531530
// . . .
532531
```
533532

534-
If you click on one of the list items in the browser window, you'll get the following information inside an alert
535-
window:
533+
If you click on one of the list items in the browser window, you'll get the information about the video in an alert
534+
window like this:
536535

537536
![Browser alert window](alert-window.png){width=700}
538537

@@ -551,7 +550,7 @@ _state_ specific to this component.
551550
State is one of core concepts in React. In modern React (using the so-called _Hooks API_), state is expressed
552551
using the [`useState` hook](https://reactjs.org/docs/hooks-state.html).
553552

554-
1. Add the following line of code to the top of the `VideoList` declaration:
553+
1. Add the following code to the top of the `VideoList` declaration:
555554

556555
```kotlin
557556
val VideoList = FC<VideoListProps> { props ->
@@ -565,7 +564,7 @@ using the [`useState` hook](https://reactjs.org/docs/hooks-state.html).
565564
* The `useState()` function from React instructs the framework to keep track of state across multiple invocations
566565
of the function. For example, even though you specify a default value, React makes sure that the default value is only
567566
assigned in the beginning. When the state changes, the component will re-render based on the new state.
568-
* The `by` keyword indicates that `useState()` acts as a [delegated property](https://kotlinlang.org/docs/delegated-properties.html).
567+
* The `by` keyword indicates that `useState()` acts as a [delegated property](delegated-properties.md).
569568
Like with any other variable, you read and write values. The implementation behind `useState()` takes care of the machinery
570569
required to make state work.
571570

@@ -662,15 +661,14 @@ the state of a parent component, you need the state lifting again.
662661

663662
In React, state always flows from parent to child. So, to change the _application_ state from one of the child components,
664663
you need to move the logic for handling user interaction to the parent component and then pass the logic in as a prop.
665-
Remember that in Kotlin, variables can have the [type of a function](https://kotlinlang.org/docs/reference/lambdas.html#function-types).
664+
Remember that in Kotlin, variables can have the [type of a function](lambdas.md#function-types).
666665

667-
1. Expand the `VideoListProps` interface so that it contains a variable `onSelectVideo`, which is a function taking a
666+
1. Expand the `VideoListProps` interface again so that it contains a variable `onSelectVideo`, which is a function taking a
668667
`Video` and returning `Unit`:
669668

670669
```kotlin
671670
external interface VideoListProps : Props {
672-
var videos: List<Video>
673-
var selectedVideo: Video?
671+
// ...
674672
var onSelectVideo: (Video) -> Unit
675673
}
676674
```
@@ -757,8 +755,8 @@ to handle this in the `App` component accordingly.
757755
}
758756
```
759757

760-
The [`let` scope function](https://kotlinlang.org/docs/scope-functions.html#let), ensures that the `VideoPlayer` component
761-
is only added when `state.currentVideo` is not null.
758+
The [`let` scope function](scope-functions.md#let), ensures that the `VideoPlayer` component is only added
759+
when `state.currentVideo` is not null.
762760

763761
Now clicking an entry in the list will bring up the video player and populate it with the information from the clicked entry.
764762

@@ -861,19 +859,19 @@ React has a rich ecosystem with a lot of pre-made components you can use instead
861859
862860
### Add the video player component
863861
864-
To replace the placeholder video component with an actual YouTube player, use the [react-lite-youtube-embed](https://www.npmjs.com/package/react-lite-youtube-embed)
865-
package from npm. It can show video and control the appearance of the player.
862+
To replace the placeholder video component with an actual YouTube player, use the `react-player` package from npm.
863+
It can show video and control the appearance of the player.
866864
867-
For the component documentation and the API description, see its [README](https://www.npmjs.com/package/react-lite-youtube-embed)
865+
For the component documentation and the API description, see its [README](https://www.npmjs.com/package/react-player)
868866
in GitHub.
869867
870-
1. Check the `build.gradle(.kts)` file. The `react-lite-youtube-embed` package should be already included:
868+
1. Check the `build.gradle(.kts)` file. The `react-player` package should be already included:
871869
872870
```kotlin
873871
dependencies {
874872
// ...
875873
// Video Player
876-
implementation(npm("react-lite-youtube-embed", "2.2.2"))
874+
implementation(npm("react-player", "2.10.1"))
877875
// ...
878876
}
879877
```
@@ -883,46 +881,47 @@ block of the build file. The Gradle plugin then takes care of downloading and in
883881
do so, it uses its own bundled installation of the [`yarn`](https://yarnpkg.com/) package manager.
884882
885883
2. To use the JavaScript package from inside the React application, it's necessary to tell the Kotlin compiler what to expect.
886-
For this, provide it with [external declarations](https://kotlinlang.org/docs/js-interop.html).
884+
For this, provide it with [external declarations](js-interop.md).
887885

888886
Create a new `ReactYouTube.kt` file and add the following content:
889887

890888
```kotlin
891-
@file:JsModule("react-lite-youtube-embed")
889+
@file:JsModule("react-player")
892890
@file:JsNonModule
893891

894892
import react.*
895893

896-
@JsName("ReactYouTubeLite")
894+
@JsName("default")
897895
external val ReactPlayer: ComponentClass<dynamic>
898896
```
899897

900898
When the compiler sees an external declaration like `ReactPlayer`, it assumes that the implementation for the
901899
corresponding class is provided by the dependency and doesn't generate code for it.
902900

903-
The last two lines are equivalent to a JavaScript import like `require("react-lite-youtube-embed").default;`. It tells
901+
The last two lines are equivalent to a JavaScript import like `require("react-player").default;`. It tells
904902
the compiler that it's certain that there'll be a component conforming to `ComponentClass<dynamic>` at runtime.
905903

906904
However, in this configuration, the generic type for the props accepted by `ReactPlayer` is set to `dynamic`. That means
907905
the compiler will accept any code, at the risk of breaking things at runtime.
908906

909907
A better alternative would be to create an `external interface`, which specifies what kind of properties belong to the
910-
props for this external component. You can infer the props interface based on the [README](https://www.npmjs.com/package/react-lite-youtube-embed)
911-
for the component — `react-lite-youtube-embed` takes a prop called `url` of type `String`:
908+
props for this external component. You can infer the props interface based on the [README](https://www.npmjs.com/package/react-player)
909+
for the component. Use props `url` of the `String` type and a Boolean `controls`:
912910

913911
1. Adjust the content of `ReactPlayer.kt` accordingly:
914912

915913
```kotlin
916-
@file:JsModule("react-lite-youtube-embed")
914+
@file:JsModule("react-player")
917915
@file:JsNonModule
918916

919917
import react.*
920918

921-
@JsName("ReactYouTubeLite")
922-
external val ReactPlayer: ComponentClass<ReactYouTubeProps>
919+
@JsName("default")
920+
external val ReactPlayer: ComponentClass<ReactPlayerProps>
923921

924-
external interface ReactYouTubeProps : Props {
922+
external interface ReactPlayerProps : Props {
925923
var url: String
924+
var controls: Boolean
926925
}
927926
```
928927

@@ -932,6 +931,7 @@ for the component — `react-lite-youtube-embed` takes a prop called `url` of ty
932931
```kotlin
933932
ReactPlayer {
934933
url = props.video.videoUrl
934+
controls = true
935935
}
936936
```
937937

@@ -1056,7 +1056,7 @@ Check the `build.gradle(.kts)` file. The relevant snippet should already exist:
10561056
dependencies {
10571057
// . . .
10581058
// Coroutines & serialization
1059-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
1059+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3")
10601060
}
10611061
```
10621062

@@ -1073,13 +1073,13 @@ types of conversions: from JSON strings to Kotlin objects.
10731073
```kotlin
10741074
plugins {
10751075
// . . .
1076-
kotlin("plugin.serialization") version "1.6.10"
1076+
kotlin("plugin.serialization") version "1.7.10"
10771077
}
10781078

10791079
dependencies {
10801080
// . . .
10811081
// Serialization
1082-
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
1082+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3")
10831083
}
10841084
```
10851085

@@ -1120,8 +1120,8 @@ invoked once the `Promise` is resolved and a result is available. However, with
11201120
Whenever a function like `await()` is called, the method stops (suspends) its execution. It continues execution once
11211121
the `Promise` can be resolved.
11221122
1123-
To give users a selection of videos, you can define the `fetchVideos()` function, which will fetch 25 videos from the
1124-
same API as above. To run all the requests concurrently, use the [`async`](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html)
1123+
To give users a selection of videos, define the `fetchVideos()` function, which will fetch 25 videos from the same API
1124+
as above. To run all the requests concurrently, use the [`async`](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html)
11251125
functionality provided by Kotlin's coroutines:
11261126

11271127
1. Add the following implementation to your `App.kt`:
@@ -1136,7 +1136,7 @@ functionality provided by Kotlin's coroutines:
11361136
}
11371137
```
11381138

1139-
For reasons of [structured concurrency](https://kotlinlang.org/docs/reference/coroutines/basics.html#structured-concurrency),
1139+
For reasons of [structured concurrency](https://kotlinlang.org/docs/coroutines-basics.html#structured-concurrency),
11401140
the implementation is wrapped in a `coroutineScope`. You can then start 25 asynchronous tasks (one per request) and wait
11411141
for all of them to complete.
11421142

0 commit comments

Comments
 (0)