setIntervalで定期的に参加者が訪れるようにする――配列の代入更新
ところで、ビデオ通話といえば最初から最後まで同じメンバーだけで進むことはあまりないですよね。途中から参加してくる人がいたり、途中で抜けていく人がいたり……。せっかくなので、そんな機能も追加してみましょう。
setInterval
を使って、20秒ごとに参加申請がポップアップし「入室許可」ボタンを押すと参加者が追加されるようにします。App.svelte
に次を書いてみます:
<script lang="ts"> ... let nextParticipant; setInterval(() => { nextParticipant = `参加者猫(${participantNames.length+2})`; }, 5000) const handleApprove = () => { participantNames = [...participantNames, nextParticipant]; nextParticipant = undefined; }; </script> {#if nextParticipant} <div id="popup"> 「{nextParticipant}」が待機中 <button on:click={handleApprove}>入室許可</button> </div> {/if} ...
ここまでの準備で、参加者の追加は単純にparticipantNames
配列に名前を追加するだけでできるようになっています。JavaScriptで配列に要素を追加するといえば、次のようにするのが普通だと思います:
participantNames.push('New Cat');
実は、これではSvelteはHTMLを更新してくれません。というのも、Svelteはプロパティやステートの更新を代入文を目印に判断しているからです。少し不自然な感じがするかもしれませんが、次のようにすることで、代入文を使いながら配列に要素を追加することができます。
participantNames = [...participantNames, 'New Cat'];
...
はスプレッド構文といって、配列の中身を展開してくれるJavaScriptの機能です。配列の中身が展開された後ろに、追加したい要素をおいた上で、新たに配列を生成しています。この新しい配列がparicipantsNames
に代入されることで、Svelteもこれが更新されたことを知ることができる、というわけです。
では、削除はどのようにすれば良いでしょうか。これも、やはり.splice()
などのメソッドを使用するとうまくいきません。次のように代入文の形にしましょう:
participantNames = participantNames.filter(name => name !== 'Cat 2');
JavaScriptの配列の.filter()
メソッドは、配列の各要素を引数として受け取る関数を評価し、その結果がtrue
になる要素だけに絞り込んだ配列を返すのでした。結果として、削除したい要素だけがfalse
になるような関数を渡してあげれば、結果的にその要素だけが削除された配列をparticipantNames
に代入することができます。
筆者の周りのSvelte入門者に聞くと、前者に比べると後者の方がまだ親しみが持てる、という人が多いようです。スプレッド構文自体、それなりに幅広く経験しないと使う場面がない機能かもしれませんね。
リアクティブステートメントを使って参加者人数を表示
さて、最後に、参加者人数を表示してみたいと思います。参加者人数を保持するnumParticipants
というステートを追加して表示してみましょう。
<script lang="ts"> ... let numParticipants = participantNames.length + 1; ... </script> ... <div>参加者数: {numParticipants}</div> ...
このコードを実行して、最初の新規参加者の入室を許可しても、参加者数の表示が更新されないことに気づかれると思います。
これは、新たな参加者が入室されたときに更新されるのがparticipantNames
配列だけで、numParticipants
は代入を受けてはいないので、Svelteがその値を更新すべきことに気づく手がかりがないためです。
handleApprove
内でparticipantNames
を更新する際に、毎回numParticipants
にも新たな値を代入するようにしても良いのですが、いかにも更新を忘れて値がズレるバグを生み出しそうです。このような場合に使うのが「リアクティブステートメント」と呼ばれるSvelteの機能です。次のように書くと、右辺に登場するステートが更新されたときに、自動的に更新されるステートを作成することができます。
$: state = anotherState + yetAnotherState;
このようにすると、anotherState
かyetAnotherState
のどちらが更新されたら、自動的にstate
も再計算してくれます。私たちの例では、次のようにすれば良さそうです:
$: numParticipants = participantNames.length + 1;
これも、export
と同じく、元々JavaScriptに存在する「ラベル」という文法を、まったく違った意味で用いている例です。といっても、export
よりはマイナーかもしれないので、戸惑うことはあまりないかもしれません。Svelteでは、const, let
のほかに$:
で変数を宣言することができて、その変数は右辺値のステートに追従して自動的に更新される、と覚えておけば良いでしょう。
ちなみに、お気づきの方もおられると思いますが、この例であれば単純にテンプレート中に{participantNames.length}
と書くことでも目的を果たすことができます。実際にはこう書いた方が良いでしょう。ここでは、リアクティブステートメントの解説をするために無駄な回り道をさせていただきました。お付き合いありがとうございます。
最後に
今回の記事では、Svelteの主要な機能をいくつか使用して、簡単なWebフロントエンドを開発してみました。Svelteの考え方や特徴を体感してただき、Svelteでの開発の楽しさを感じていただければ嬉しいです。今回のコードの全体像はGitHubにて公開しています。
Svelteにはライフサイクルコールバックやアニメーションなど、まだまださまざまな豊富な機能があります。今回の記事はコンパクトにSvelteを体験することを目的としたため網羅していませんでしたが、Svelteの公式にはオンラインで実行しながら学べる楽しいチュートリアルがあります。日本語化もされているので、ぜひチャレンジしてみてください。
ちなみに、チュートリアルの日本語化はSvelte日本のコミュニティのメンバーによるものです。日本語化の中心となったtomoamさんには、本記事のレビューにもご協力くださいました。Svelteの日本コミュニティは、日本のSvelteファンが集まるDiscordを中心に活動しています。ご興味のある方はぜひご参加ください。
また、本記事に関してわからないところや気になるところなどがあれば、Twitterで@hmgchkにご質問いただいても構いません。
最後に、草稿をレビューしてくれた小関さん・Svelteコミュニティのtomoamさん、参考コードのテストプレイに協力してくれた別府さん・柏原さん・西川さん・本田さんに、この場を借りて感謝を申し上げます。
次回は、フロントエンドライブラリであるSvelteを使ってより本格的なアプリケーションを開発するためのフレームワークSvelte Kitを使用して、簡単なWebアプリを作成し、外部からアクセスできるようにデプロイするところまでを紹介する予定です。
参考
- Kevin Bridges - Responsive Svelte (exploring Svelte's reactivity)
- Alessandro Segala (2020) *Svelte 3 Up and Running* Packt Publishing
記事に使用したemojiのクレジット
Twemoji” by Copyright 2021 Twitter, Inc and other contributors is licensed under CC-BY 4.0