<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Saki Basken - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ エキスパートの手によるプログラミングチュートリアル記事を幅広く掲載。ウェブ開発、データサイエンス、DevOps、セキュリティ、開発者としてのキャリアなどについて学びましょう。 ]]>
        </description>
        <link>https://www.freecodecamp.org/japanese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Saki Basken - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/japanese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 25 May 2026 04:46:03 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/japanese/news/author/saki/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ エンジニアへ転身するための完全ガイド: 在宅フリーランスと育児から開発者の道へ ]]>
                </title>
                <description>
                    <![CDATA[ 半年以上にわたり寝る間も惜しんで取り組んだ求人応募、ネットワーキング、勉強の末、晴れて今年 1 月にエンジニアとしてのフルタイムの仕事のオファーを受けました。それは決して簡単な道ではなく、正直に言えば、かなり荒れた獣道のようでした。 私のように仕事を探している人や私と同じようにテック業界に転身しようとしている人がたくさんいると思います。 就活中に何度も挫折しそうになりましたが、先にテックに移行した方々の話を聞くことはとても参考になりました。だからこそ、私がやって良かったなと思うことをいくつか共有して、少しでも同じ道を歩んでいる方々のサポートになればと思っています。 これまでの経歴 まずはこれまでの私がしていたことや、どのようにしてエンジニアとして働くようになったのかについて少し話したいと思います。私はコンピュータサイエンスのバックグラウンドを全く持っていません。もともとは生粋の文系で、大学では人類学と社会学を専攻して、興味深い分野で自分にとってとても合った分野だったのですが、就職の見通しはあまり良くありませんでした。 大学を卒業した後、社長秘書や倉庫 / 輸入のコーディネーター ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/from-stay-at-home-freelancing-parent-to-developer-tips/</link>
                <guid isPermaLink="false">66dca971e8acb3045418de39</guid>
                
                    <category>
                        <![CDATA[ Tech Journey ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Career Change ]]>
                    </category>
                
                    <category>
                        <![CDATA[ WomenInTech ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer-Journey ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MomsInTech ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Saki Basken ]]>
                </dc:creator>
                <pubDate>Tue, 22 Oct 2024 06:01:21 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2024/09/unseen-studio-s9CC2SKySJM-unsplash--1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>半年以上にわたり寝る間も惜しんで取り組んだ求人応募、ネットワーキング、勉強の末、晴れて今年 1 月にエンジニアとしてのフルタイムの仕事のオファーを受けました。それは決して簡単な道ではなく、正直に言えば、かなり荒れた獣道のようでした。</p><p>私のように仕事を探している人や私と同じようにテック業界に転身しようとしている人がたくさんいると思います。</p><p>就活中に何度も挫折しそうになりましたが、先にテックに移行した方々の話を聞くことはとても参考になりました。だからこそ、私がやって良かったなと思うことをいくつか共有して、少しでも同じ道を歩んでいる方々のサポートになればと思っています。</p><h2 id="-">これまでの経歴</h2><p>まずはこれまでの私がしていたことや、どのようにしてエンジニアとして働くようになったのかについて少し話したいと思います。私はコンピュータサイエンスのバックグラウンドを全く持っていません。もともとは生粋の文系で、大学では人類学と社会学を専攻して、興味深い分野で自分にとってとても合った分野だったのですが、就職の見通しはあまり良くありませんでした。</p><p>大学を卒業した後、社長秘書や倉庫 / 輸入のコーディネーターとして一般に言う事務の仕事をしていました。私は日本育ちですが、高校で短期留学、大学でイギリスに行っていたりしたことから、シカゴの日系企業で働いていた時に仕事の一環として翻訳作業をするようになりました。</p><p>2013 年に長女が生まれた後、当時働いていた会社では産前産後を含め、産休はなんと 6 週間しかありませんでした。当時のイリノイ州では標準的なことでした。10 年以上も前のことなので、今は少しは改善されているのかもしれませんが、産休が 6 週間しかないことにただただ驚いたのを今でも鮮明に覚えています。</p><p>そのため、お給料も格別良かったわけではなかったこと、夫とも子供は二人は欲しいねと話していたことも考えて、フルタイムのポジションを辞めて子育てに集中し、フレキシブルに働けるフリーランスの翻訳者として働くことに決めました。</p><p>なので学歴を含む経歴は、全くプログラミングとは程遠く、パソコン関係のスキルといえばマイクロソフトのオフィス程度で当時の私はエクセルが最先端のパソコンのスキルでした。</p><p>今も当時も、環境問題に興味があるので、「持続可能な開発」と言う分野を勉強して、ロンドン大学の準修士号を取得しました。言ってしまえば、出産育児の為修士号を取得するための単位がわずかに不足してしまったと言うことです。そのプログラムを通して環境科学やその他のサイエンス関連の授業を受けましたが、そこでもプログラミングに触れる機会はなく、私はコンピュータや理系向きではないとすら考えていました。</p><h2 id="--1">子育てとフリーランス: 見えないキャリアの空白</h2><p>2 人の娘を育てながらフリーランスで翻訳をして平穏な日々を過ごしていましたが、子供達の小学校入学後は、自分の時間も増えてきてもっと仕事をしてみたいと感じました。『会社で働きたいな。』と漠然と思うようにもなりました。産後何をしたいかを考えたとき、今あるスキルをそのまま使えたからという理由で翻訳はやっていたけれど、翻訳者としてフルタイムで働く気にはどうもなれませんでした。</p><p>私は環境問題について興味を持っていたので、環境問題関連の仕事をしたいと思いました。数ヶ月間仕事を探しましたが、アメリカの一次面接でよくある電話面接をどうしても突破することができませんでした。その 15 分から長くても 30 分位の短い通話の間に、私はよく 10 年以上前のオフィスで働いていた時の経験について聞かれました。</p><p>特に最初の面接は辛いものでした。私が今何をしていて、今何ができるかに興味を持ってもらえると思っていたので、まさか 10 年前のはるか昔の経験について尋ねられるとは思っていませんでした。そんな面接をする度、自分が過去 10 年間してきたことがあまり重要ではないと言われたように感じ、かなり落ち込むこともありました。</p><p>育児やフリーランスは非常に大変な仕事だと思います。時間、仕事、ストレス、顧客、請求書などをすべて自分で管理しなければなりません。それでも社会や企業は、専業主婦やフリーランサーが私たちの社会に対して行う仕事を見落としてしまいがちですね。Career break と言われていることすら、問題視されるべきかもしれません。決して break とは感じられませんでしたので！</p><p>しかも 10 年以上も前の経験を使って behavioral question (行動面接の質問) に細かく答えることなんて、AI でない限り無理ですよね。</p><h3 id="--2">転機: 私がコーディングに興味を持ったきっかけ</h3><p>そんなこんなで、就活をしながら環境関連のテック企業が必要とするスキルを持っていないことにも気付いてしまいました。まず、そういった企業はインハウスの翻訳者を大抵募集していない上に、一般事務的な経験もそこまで年数があるわけでもないと言うとても残念な事態。</p><p>多くの環境テック系企業がソフトウェアエンジニアを求めているのに気づき始めた頃、夫がたまたま私にコーディングを勉強してみたら？と言ってきました。それに加えて、第二のシリコンバレーと言われるだけあって、私の住むシアトルはエンジニアが本当に多いんです。出会う二人に一人はエンジニアです。そんな影響もあり、就活も長期戦になりそうだったので、損することはないだろうと軽い気持ちで勉強を始めました。</p><h2 id="--3">初めの一歩: 技術翻訳者としての初めての経験</h2><p>夫がスタートアップを持っているので、その会社のソフトウェア開発を担当している女性にすぐ連絡を取りました。と言うのも、何から始めたらいいか全くわからなかったからです。彼女はそのスタートアップの創立者三名のうち一人のお姉さんであり、私たち夫婦の友人でもあります。彼女に私がどこから始めるべきかを聞いたら、実際に仕事をしてみることが一番勉強になると教えてくれました。そして彼女の指導のもと、スタートアップでお手伝いをさせてもらえることになりました。</p><p>翻訳のバックグラウンドと新しいテックへの興味から、私はモバイルアプリの技術的なローカライゼーション (他言語でも使用可能にすること) を始めました。大学で CS を勉強したわけではなかったけど、それが私の最初のソフトウェアエンジニアのポジションとなりました。でもそれはファミリービジネスで、走り始めたばかりのスタートアップなので、給料はもちろんもらえませんでした。とはいえ、コーディングだけでなく、git (コードのバージョン管理) についても学べて、ソフトウェア開発の流れを学ぶ絶好のチャンスでした。</p><p>何でもいいんです。ご家族や友人のビジネスで、ウェブサイトの更新や全面的なリニューアルが必要か確認してみてください。どんなことでも、手をかけて何かを作り、それを通じて学び、成果を示すことができれば、それが経験になります。</p><p>お手伝いをしながら、もっと学びたいと感じたので、2022 年の感謝祭セールで Codecademy に半額割引で登録して、そこで思いつくすべてのレッスンを制覇しました。(その当時残念ながら freeCodeCamp で無料で勉強できると知らず、Codecademy で登録してしまいました。freeCodeCamp.org は無料なので、プログラミング興味のある方にとってもオススメです！)</p><p>彼女と一緒に働かせてもらいながら、独学でプログラミングの勉強もして、充実していました。彼女は今でもすばらしいメンター (サポートやアドバイスをしてくれる先輩) です。その時点で、私はこれを自分のキャリアにし、もっと学びたいと感じました。</p><p>私は興味を持つと、いつも深く突き進むタイプなので、プログラミングの前は韓国ドラマにハマり、その前は株式市場の勉強も独自でしたりしていました。普段は大体 3、4 ヶ月間で興味が失せてしまうんですが、プログラミングは自分でも驚く程飽きませんでした。良くも悪くもプログラミングには無限に問題があって、常に学習していないといけないので、勉強好き、新しいことへの挑戦好きな私にとっていい分野を見つけたのではないかと思っています。</p><p>こういった経緯で修行をさせてもらっている中、ある日夫が前住んでいた家のお隣さんの家に遊びに行き、たまたまそこの奥さんが Flatiron というスクールでプログラミングのブートキャンプ式プログラムを卒業して、それがすごく良かったという話を聞いてきました。彼女も私と同じようなキャリアチェンジャーで、その後エンジニアとして働いています。夫がその話を家に持ち帰ったなんと翌日にはすぐに同じプログラムに申し込みをしました。</p><h2 id="--4">大きな決断: 血と汗と涙のブートキャンプの日々</h2><p>結果的に、プログラミングのブートキャンプはとても良かったです！その名前の通りブートキャンプなので非常に厳しいものでしたが、一人で学んでいるときに週に 60 時間も費やすことはありませんし、学習のスピードをかなり早めてくれました。</p><p>始めた時点では、クラスに 30 人以上の学生がいましたが、1 週間後には 25 人、次の週には 20 人と、みるみるうちに脱落者が続出しました。卒業時にはクラスメートは 12 人だけになってしまいました。すごくハードなスケジュールで精神的にも追い詰められるような時もあったし、トラウマになるんじゃないかと思うようなこともあったり。でもそんな経験を一緒に乗り越えた仲間たちなので、そこで出会ったクラスメートたちには特別な想いがあります。</p><p>ブートキャンプといっても、その編成には色々あって、私のブートキャンプは 15 週間のプログラムで、各フェーズが 3 週間の 5 フェーズに分かれていました。各フェーズでコーディングチャレンジと言われるテストとグループプロジェクトがあり、プログラム最後のフェーズにはソロプロジェクトがありました。</p><h3 id="--5">ブートキャンプ後のサポート: キャリアコーチと就活戦略</h3><p>Flatiron では、卒業後 180 日間のキャリアサービスを受けることができます。学費は高いと思いましたが、私は面接にこぎつけることや、面接に進めても面接自体で挫折していたので、それが魅力で Flatiron を選びました。</p><p>ワークショップなどを含むキャリアサービスは、プログラム中に始まりました。卒業後は、専属キャリアコーチが割り当てられ、最初の月は週 1、その後は隔週で面談しました。私の LinkedIn プロフィールをチェックしてくれたり、履歴書を添削してもらったり、面接前には模擬面接で特訓など、仕事探し全てにおけるサポートをしてくれました。</p><p>また、Flatiron には「Global Community」というサブグループもあったので、私はプログラム中やプログラム終了後も積極的にそのグループのオンラインミーティングにも参加していました。</p><p>人数が少なかったので、同じ外国人の方や、多文化の環境で育った方々と一緒にキャリアに関することを教えてもらったり、先輩の話を聞いたり、不安を分かち合ったりできる場でもあり、かなり勉強になりました。</p><p>2023 年 1 月に私がプログラムを開始したときには、Flatiron のキャリアサービスの人に『平均で卒業後就活は 3〜6 か月かかる』と言われていましたが、その時期大手テック企業で大量のリストラが続いていたので、卒業時には平均で 6 か月はかかると言われるようになっていました。</p><p>私が就職するまでには実際に 7 か月かかりました。その間多くのネットワーキングイベントに参加しましたが、私の就職にとって、キャリアコーチとの時間は本当に必要不可欠だったと思っています。質問ができる人やアドバイスをくれる人がいるということはスキルの面でも、精神的なサポートとしても、本当に助けになりました。</p><h2 id="--6">転機と躍進: 初めてのフルタイムの仕事を得るまで</h2><p>卒業後すぐに、就職活動を開始しました。ボランティア活動やオープンソースプロジェクトへの貢献、ネットワーキングイベントへの参加を通じて学び続け、AWS CP 試験にも合格しました。忙しい日々を送りながら、LinkedIn でも積極的に活動し、プログラミングやサステナビリティに関連するマイルストーンを投稿していました。</p><p>Flatiron のキャリアワークショップで、求人の 80 % は公募されず、多くの人が人脈や過去のコラボレーションを通じて仕事を見つけると学びました。当初は半信半疑でしたが、まさにその方法で私は初めての仕事を得ました。</p><p>夫のスタートアップで一緒に働いていたメンターが、LinkedIn で私のシェアした React で作成したフルスタックのウェブアプリを見てくれました。彼女の会社が携わっているプロジェクトで React 開発者を必要としていた時、彼女は私に直接連絡をくれ、仕事のオファーをくれたのです。</p><p>この経験を通じて、私にとって大きな成長につながるポイントがいくつかあったので、私が役立ったことをいくつか共有したいと思います。</p><h3 id="--7">ネットワーキング: ぎこちなさを克服し、関係を築く</h3><p>ネットワーキングって必要なの？と思う方も多くいるかもしれませんが、私の就活ではネットワーキングはキャリアコーチとの時間と同じ位重要なものでした。そのようなイベントに出たからといって、速攻で仕事を貰えるということはありませんが、面接の練習をする場として、本当に役立ちます。</p><p>ブートキャンプのプログラム在籍中は、meetup.com や LinkedIn などのウェブサイトで見つけた地元のイベントに色々参加しようと努力はしていたものの、勉強が大変でなかなか時間が取れませんでした。コーディングの課題提出の前日にイベントがあったり、プロジェクトで切羽詰まっている状態だったり、なかなか参加できませんでした。しかし、卒業後はすぐに仕事を探し始めたかったので、プログラム中に卒業後のイベントをどんどん予約していました。</p><p>今回就活をするまで、私は今まで同業界の人たちと繋がる場に積極的に行くというようなネットワーキングをした経験は全くありませんでした。Flatiron 在籍中に、それはとても重要だとコーチに何度も言われていたので、重要性は把握してたのですが、自分の中で勝手に『採用イベント』なんだと誤解してしまっていました。</p><p>正直、初めて行ったネットワーキングイベントはもう気まずかったとしか言いようがありません。誰も知らない人が 100 人ほど集まっている結構大きめのイベントで、仕事を見つけると言う目的の婚活パーティーのスピードデーティングのような感じで、できるだけ多くの人と会話しなければならないと勝手にプレッシャーを感じていました。そんな状況が居心地いいはずはないですよね。</p><p>ほとんどの団体やグループは月に 1 回など、定期的にミートアップやイベントを開催しているので、私は大体 10 グループのミートアップに毎月参加するようにしていました。環境系テック業界に興味があったので、環境テックのイベントや開発者向けのイベントなどに参加していました。</p><!--kg-card-begin: html--><h4>ネットワーキングから学んだこと: 会話をチャンスに変える方法</h4><!--kg-card-end: html--><p>ネットワーキングのイベントは決して「採用イベント」ではありません。30 人以上の人に会って、30 人以上の新しい LinkedIn のつながりを作る必要は全くありません。少数でも意味のある会話をして、覚えていられる人達との良いつながりを作ることに焦点を合わせると、より意味のあるものになるんじゃないかと思います。</p><p>そういう場で自然と次々に色々な人と話して行くと言うのが得意な方もいると思うので、人によって得意不得意を見極め、自分に合うスタイルで参加して行くことがオススメです。</p><p>同じ団体やグループが主催するイベントに毎回参加していると、だんだん顔なじみが増えてきて、行くたびに楽しさが増していきます。こういったイベントは直接仕事につながるわけではないかもしれませんが、私にとっては面接の練習にもなり、就活の大変さがわかる仲間に出会える場でもありました。</p><p>初対面の人に「お仕事は何をしているのですか？」と聞かれたり、会の初めに「簡単な自己紹介をお願いします。」と言われる事がよくありました。これって面接官が聞く質問ですよね。それで、面接官の方もただ面接を受ける人のことを知りたいだけなんだ！と思ったらそれまで尋問のように考えていた面接も、かなり気が楽になったのを覚えています。</p><p>また、同じ興味を持つ人と話すことで、自分が知らなかったリソースを教えてもらうことも多々ありました。</p><p>例えば、私はシアトルの Amazon AWS スキルセンターが、世界中どこからでもアクセスできる AWS のクラス (対面＆オンライン) を無料で提供していることを知りました。また、他のエンジニアの方に、環境テック関連の中小企業は人をトレーニングする資金や人材が不足しがちなため、経験豊富な人を雇うことが多いということも教えてもらいました。さらに、大手テック企業がブートキャンプ卒業生など、CS (Computer Science) の学位を持たない人向けにやっているプログラム (Microsoft Leap Program など) の存在も教えてもらいました。</p><p>かといって、ネットワーキングのために必ずしも知らない人ばかりのミートアップに参加する必要はないと思います。子供を学校に迎えに行ったときに他の保護者の方と話したり、友達や近所の人と「最近どう？」といった日常の会話の中で『今プログラミング勉強してるよ。』とか、『仕事探してるよ。』と伝えるのも一つだと思います。プログラミングを学んでいることや仕事を探していることを周囲に伝えてみてください。</p><p>人は助けられるなら助けたいと思うものです。それがブログを書いたり、オープンソースプロジェクトに貢献したり、リソースを共有したりする一番の理由だと思います。すぐには結果が出ないかもしれませんが、いい機会があれば、そういった人が教えてくれるでしょう。</p><p>実際、私もオファーを受けた後に、友人から彼女の部署でバックエンドエンジニアのポジションの募集があると声をかけてもらった経験があります。それは私が、ブートキャンプを卒業して仕事を探していること、バックエンドがすごく気に入っていることを世間話のように話していたからこそあったチャンスだと思います。</p><p>残念ながらその時はもうすでに仕事のオファーを受けていたので断ることになってしまったのですが、ネットワーキングによって、まだ公募していないポジションに出会える事だってあるのです！</p><h3 id="--8">求人応募の攻略と企業へのアプローチ法</h3><p>昨年 2023 年の米国ジョブマーケットではインタビューを得ることはかなり大変でした。特に、私のように 1 つの分野で 10 年以上の経験があるにもかかわらず、完全に新しい分野に挑戦しようとしている人にとってはなおさらです。</p><p>実際に、翻訳関係の仕事は申し込むとすぐ面接に繋げられ、一つは一次面接をしたものの、こちらからどうしてもやる気がでずお断りしてしまったほどでした。</p><p>テック業界では、エントリーレベルの職種でさえも 2 年、時には 4 年以上の経験を求められることがよくあります。謎すぎます。その上たとえその条件を満たしていても、5 次面接までさせられたり。テック業界はかなり疑心暗鬼のようです😂</p><p>私にとって仕事に応募する際の鍵となった戦略は、応募後に必ず採用担当の方やその会社の誰かにメッセージを送ることでした。企業は、本当にその会社や仕事に関心を持っている人を採用したいと考えています。自分の興味やなぜ適任であるか、スキルがどのように役立つかを伝える人間味のあるメッセージを送ることで、彼らの目に留まる可能性が高くなります。</p><p>もしその会社に知り合いがいるなら、通常の方法で応募するのではなく、まずその人に連絡を取り、推薦してもらえるかどうかを確認してみることも大切です。私が 7 ヶ月間に得た面接の約 90 % は、そういったメッセージからこぎつけたものでした。</p><p>また、メッセージを送ることで、その会社の文化を垣間見ることもできます。ある会社はまるで自動返信のように、「そういったご用件でしたら、ウェブサイトで直接ご応募ください。」とだけ返ってきたり、無視される事が大半でしたが、その中でもとても親切な対応をしてくれる会社の方々もあり、一層こんな優しい人ばかりの会社で働きたい！と思ったのを覚えています。</p><p>厳しい就活環境では選り好みするのは難しいですが、私は働きやすい文化を持つ会社で働きたいと思っています。連絡を取ることで、面接にもつながる可能性が高くなる上、その会社がどんな雰囲気か少し感じ取ることができました。</p><h3 id="--9">面接突破法: 会話を仕事のオファーに繋げる方法</h3><p>五万とある応募の中から選んでもらって面接を取り付けるまでが一番難しいと思いますが、普段ネットワーキングイベントに参加している人なら、事前準備をしていれば面接自体は大抵うまくいくと思います。</p><p>例えば、私は Amazon だけでも恥ずかしながら 100 以上のポジションに応募し、その中で 25 件ほどは推薦で応募しましたが、実際に面接をしてもらえたのはたった 1 つのポジションだけでした。そう、たった 1 つだけなのです。他は全部書類面接で経験が足りず落とされてしまいました。</p><p>結局カリフォルニアへの引越しが出来ず内定をお断りすることになってしまったのですが、その 1 つのポジションで、合計 5 時間以上の面接の後オファーをもらえたので、通い詰めたネットワーキングイベントのおかげで、面接獲得後の成功率は 100 % でした<strong>😎</strong></p><p>私が色々なポジションの面接を受けて感じたのは、面接官の方はあなたが何をできるかということよりも、『どのように考えるか』と言う事をもっと重視しているということです。その職種、会社、ポジションにもよると思いますが、私の経験では質問は常にあなたの思考プロセスや、過去にどのような状況でどのように対応したかについてでした。</p><p>何ヶ月もネットワーキングイベントに参加したことで、面接官との会話が自然にできるようになり、リラックスして臨めました。Flatiron に入る前の面接を振り返ると、あれは会話というよりも完全に取り調べにあっているようでした。</p><p>面接は、面接官の人達が一緒に働きたい人を見つけるためのものなので、会話をするように心がけると自然にやり取りができました。もし月曜日の面接なら、「週末はどうでしたか？」と尋ねるようにしていました。「元気ですか？」と聞かれたら、「元気です、ありがとうございます。あなたはどうですか？」とよくある英語の受け答えではなく、週末に何をしたのか、ペットや子供について、体調を崩していたことなど、何か個人的なことを話しました。</p><p>日本の面接だったら無駄話が多いと印象付いてしまうかもしれませんが、アメリカではこういった人間らしい会話をすることで、他の面接を受ける人と差別化を図ると、相手に強い印象を残せ覚えてもらえます。こちらが長い返答をすると、自然と相手も長い返答をしてくれ、お互いをもっと知る機会となります。</p><h3 id="--10">トップから学ぶ: メンターシップが技術の成長を加速</h3><p>冒頭で 1 人のメンターについて触れましたが、実際には数人のメンターがいました。メンターは本当に素晴らしい存在で、これはテックカルチャーの一部なのか、それとも西海岸の文化なのかはわかりませんが、サポートしてくれる人が本当にたくさんいます。</p><p>1 人目のメンターは、前にもお話しした通り、私をこの道に導いてくれた人で、彼女はソフトウェア会社を経営しており、現在私は彼女の会社で働いています。</p><p>もう 1 人のメンターは、昨年 PNW Climate Week でボランティアをした際知り合った方です。イベントが終わった後も、就職が決まるまで 2 週間に一度オンラインで面談していました。彼女は私の就職活動の進捗を気にかけてれて、その二週間で私が就活でしたことを確認し、アドバイスをくれました。</p><p>私はとても働きたいと思っていた会社から面接の一環で課題をもらったのですが、残念ながらその課題提出後不採用となってしまいました。今となっては恥ずかしい話なのですが、自分では結構課題の出来に自信があったので、なぜ不採用になったのかわからず、かなり落ち込んでいました。彼女にその課題を見せると、シニアエンジニアとして色々助言をくれて、それまでわからなかった不採用の理由が彼女と話すことで明確になりました。彼女との会話は本当に勉強になりました。そういう機会を隔週で設けてもらえたことで、面接官と話す特訓にもなっていたと今では思っています。ボランティアでここまでしてもらえて、本当に感謝しかありません。</p><p>3 人目のメンターは、なんと LinkedIn を通じて知り合った人でした。私は LinkedIn で積極的に活動しており、しょっちゅう投稿していたので、彼はたまたま私を見つけて「メンターをするのが好きだから」と言って向こうから申し出てくれて、次になんのプログラミング言語を勉強したらいいか、このサイトでプロフィールを作った方がいいよなど、あらゆる指導や相談に乗ってくれました。プロフィールを作ると今度はその内容を添削してくれたりもしました。</p><p>テック業界にはサポートしてくれる人がたくさんいるので、遠慮せずに周りの人に聞いたり、そういったオファーがあればぜひ遠慮しないで受けてみてください。世の中には本当に素晴らしい人たちがいて、かなり助けてもらったので、私もいつかどこかの誰かに同じことをすることで恩返しできるといいなと思っています️。</p><h3 id="--11">オープンソースの可能性: 経験と信頼性の構築</h3><p>オープンソースとは、誰でも自由にアクセスできるソフトウェアのことを指します。コードが公開されていて誰でも閲覧、修正、機能追加などができます。多くの人が共同で開発に参加することで、ソフトウェアの品質向上や新しいアイデアの実現が促進されます。</p><p>GitHub などのプラットフォームを通じて、世界中のエンジニアがオープンソースプロジェクトに貢献しています。実際のコード関連のオープンソースだけでなく、freeCodeCamp.org では、テック関連の学習内容を翻訳するオープンソースプロジェクトもあります。</p><p>そのようなオープンソースプロジェクトをまだ始めていなくて、テック業界の経験があまりないのであれば、オープンソースプロジェクトへの貢献をできるだけ早く始めることをお勧めします。ブートキャンプ中のキャリアワークショップで、オープンソースは業界経験がない人にとって特に価値があると言うことを学びました。卒業する頃には、すでに 2 ヶ月以上の実務経験を積んでいることだって可能になります。大学生なら数年の経験だってあり得ます。</p><p>私は昨年の 3 月、Flatiron 在学中にオープンソースプロジェクトへの貢献を始め、その最初のプロジェクトの 1 つがこの freeCodeCamp.org でした。</p><p>freeCodeCamp.org のプロジェクトの素晴らしいところは、私が記事に取り組み始めるとすぐに、以下のようなプロフィールページを作成してくれたことです。それを履歴書やポートフォリオにすぐに活用することができました。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/10/fcc_profile.png" class="kg-image" alt="fcc_profile" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/10/fcc_profile.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/10/fcc_profile.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/10/fcc_profile.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="1209" loading="lazy"><figcaption><a href="https://www.freecodecamp.org/japanese/news/author/saki/">freeCodeCamp.org のプロフィール</a></figcaption></figure><p>Meta の React プロジェクトへの貢献は、私の GitHub のアクティビティーのステータスを緑に保つのにも役立ちました。Git の使い方もわからないところを経験豊富なモデレーターのエンジニアの方に教えもらったり、とても勉強になりました。</p><p>ブートキャンプを卒業した後、環境テック系のイベントで The Climate Tech Handbook というオープンソースプロジェクトの創設者に出会い、React のディベロッパーを探していると聞き、すぐに参加させてもらいました。張り切ってまるでパートタイムの仕事のように取り組んでいました。そのプロジェクトを通して、ウェブデザイナーや Figma デザイン、さまざまなチームと協力して働くようになり、プログラミングのコードだけでなく、Software Development という分野の開発サイクルを勉強することもできました。</p><p>これらの経験はポートフォリオや履歴書に追加できるだけでなく、面接でも大いに活用することができました。面接で聞かれたほとんどの質問には、これらの経験を基に答えることができました。</p><p>テック業界での経験がまだ浅かったにもかかわらず、非常に関連性の高い話題を話す事ができたのです。そしてもちろん、これらの経験を履歴書に追加することで、同じキャリアレベルの他の人と比べて差別化することもできます。</p><p>freeCodeCamp、The Climate Tech Handbook、React Project をはじめ、他のオープンソースコミュニティのおかげで、ポートフォリオを豊かにする事ができました。自分一人では到底達成できなかったことだと思います。心から感謝です。</p><h2 id="--12">テックジャーニーを振り返って: 型破りな道を楽しむ</h2><h3 id="--13">キーポイント</h3><ol><li>積極的に行動することは、履歴書とポートフォリオの内容の改善に大いに役立ちました。ボランティアやオープンソースプロジェクトへの貢献は、同じ経験レベルの候補者の中で差別化を図る素晴らしい方法で、面接時に使える貴重な題材にもなります。</li><li>できるだけ多くの人と出会い、新しいリソースを発見し、アドバイスをもらったり、面接スキルを向上させましょう。これらを続けることで、新しい業界で特に感じがちな、「<a href="https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%B3%E3%83%9D%E3%82%B9%E3%82%BF%E3%83%BC%E7%97%87%E5%80%99%E7%BE%A4">インポスター症候群</a>」を和らげる助けにもなります。</li><li>学び続けることは大事ですが、無理のないペースで行うことが重要です。私のメンターの 1 人が言っていたように、学びにも「季節」があります。この記事を読んでいるのであれば、あなたも私のように常に学び続けていないと罪悪感を感じるタイプの人かもしれません。ですが、学んだことを消化するには時間がかかること、休息は学習の過程の一部として必要不可欠だということを忘れないようにしましょう。</li><li>他の分野から技術業界に転職することは、意外かもしれませんが実は大きな強みになると思っています。あなたの独自の背景のおかげで、他の人が持っていない視点から物事を見ることができますし、私が技術と翻訳スキルを活かして専門分野に踏み出したように、あなたしかできない分野があるはずです。</li></ol><p>メンターの紹介で入ったソフトウェア開発の会社で働き始めて、早くももう 9 ヶ月が経ちました。本当にあっという間で、毎日多くのことを学びながら、仕事を楽しむことができています。ひょんなことから始めたプログラミングでしたが、数年前にこの新しいテックという分野に挑戦して本当に良かったと思っています。</p><p>仕事がすぐに見つからなかったことは精神的にも辛く、時には心が折れそうになることもありましたが (アメリカで就活経験がある人は共感してくれる方も多いかもしれません)、そのおかげで今の自分があると思うと、結果的にはそれで良かったんだと思います。これからの自分の成長が楽しみです。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React、Node、Socket.io、HarperDB を使用してリアルタイムのチャットアプリケーションを作成する方法 ]]>
                </title>
                <description>
                    <![CDATA[ この記事では、Socket.io と HarperDB を使用して、チャットルームを備えたフルスタックのリアルタイム・チャットアプリケーションを作成します。 このプロジェクトを通して、フルスタックアプリケーションの作り方や、バックエンドがフロントエンドとリアルタイムで通信できるウェブアプリの作り方を学ぶことができます。 通常、HTTP リクエストを使用する場合、サーバーはリアルタイムでデータをクライアントにプッシュすることはできません。しかし、Socket.io を使用することで、サーバーはサーバー上で発生したいくつかのイベントに関するリアルタイム情報をクライアントにプッシュすることができるようになります。 これから作成するウェブアプリには 2 つのページがあります。 チャットルームに参加するページ。 そしてチャットルームのページ。 このウェブアプリ作成に使用するものは次のとおりです。  * フロントエンド: React [https://reactjs.org/docs/create-a-new-react-app.html]     (インタラクティブなアプリケーショ ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/</link>
                <guid isPermaLink="false">65b3ea0f6e329503f6305828</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Socket.io ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HarperDB ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Saki Basken ]]>
                </dc:creator>
                <pubDate>Wed, 28 Feb 2024 09:31:42 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2024/01/pexels-keira-burton-6146929.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Build a Real-time Chat App with React, Node, Socket.io, and HarperDB</a>
      </p><p>この記事では、Socket.io と HarperDB を使用して、チャットルームを備えたフルスタックのリアルタイム・チャットアプリケーションを作成します。</p><p>このプロジェクトを通して、フルスタックアプリケーションの作り方や、バックエンドがフロントエンドとリアルタイムで通信できるウェブアプリの作り方を学ぶことができます。</p><p>通常、HTTP リクエストを使用する場合、サーバーはリアルタイムでデータをクライアントにプッシュすることはできません。しかし、Socket.io を使用することで、サーバーはサーバー上で発生したいくつかのイベントに関するリアルタイム情報をクライアントにプッシュすることができるようになります。</p><p>これから作成するウェブアプリには 2 つのページがあります。</p><p>チャットルームに参加するページ。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/home-page.JPG" class="kg-image" alt="home-page" width="475" height="468" loading="lazy"></figure><p>そしてチャットルームのページ。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/chat-page.JPG" class="kg-image" alt="chat-page" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/chat-page.JPG 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/chat-page.JPG 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/chat-page.JPG 1216w" sizes="(min-width: 720px) 720px" width="1216" height="678" loading="lazy"></figure><p>このウェブアプリ作成に使用するものは次のとおりです。</p><ul><li><strong>フロントエンド</strong>: <a href="https://reactjs.org/docs/create-a-new-react-app.html">React</a> (インタラクティブなアプリケーションを構築するためのフロントエンド JavaScript のフレームワークです)</li><li><strong>バックエンド</strong>: <a href="https://nodejs.org/en/">Node</a> と <a href="https://expressjs.com/">Express</a> (Express は、API とバックエンドを簡単に作成できる非常に人気のある NodeJS のフレームワークです)</li><li><strong>データベース</strong>: <a href="https://harperdb.io/">HarperDB</a> (SQL または NoSQL を使用してデータをクエリできるデータ + アプリケーションのプラットフォーム。HarperDB には API も組み込まれているため、大量にバックエンドのコードを書く必要がなくなります)</li><li><strong>リアルタイム通信</strong>: <a href="https://socket.io/docs/v3/">Socket.io</a> (以下を参照してください！)</li></ul><p> <a href="https://github.com/DoableDanny/Realtime-chat-app-with-rooms">ソースコードはここをご覧ください</a>。(星を付けるのをお忘れなく⭐)</p><h2 id="-">目次</h2><ol><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#what-is-socket-io">Socket.io</a> とは</li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#project-setup">プロジェクトのセットアップ</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-build-the-join-a-room-page">「チャットルームに参加する」ページ作成方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-set-up-the-server">サーバーの設定方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-create-our-first-socket-io-event-listener-on-the-server">サーバー上で最初の Socket.io イベントリスナーを作成する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-rooms-work-in-socket-io">Socket.io のルームの仕組み</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-build-the-chat-page">チャットページを作成する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-create-the-messages-component-b-">メッセージコンポーネント (B) を作成する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-create-a-schema-and-table-in-harperdb">HarperDB でスキーマとテーブルを作成する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-create-the-send-message-component-c-">メッセージ送信コンポーネント (C) の作成方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-set-up-harperdb-environment-variables">HarperDB の環境変数を設定する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-allow-users-to-send-messages-to-each-other-with-socket-io">Socket.io を利用してユーザーが相互にメッセージを送信できるようにする方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-get-messages-from-harperdb">HarperDB からメッセージを取得する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-display-the-last-100-messages-on-the-client">クライアントで最新メッセージ 100 件を表示する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-build-the-chat-page">チャット</a><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-display-the-room-and-users-a-">ルームとユーザー (A) の表示方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-remove-a-user-from-a-socket-io-room">Socket.io のルームからユーザーを削除する方法</a></li><li><a href="https://www.freecodecamp.org/news/build-a-realtime-chat-app-with-react-express-socketio-and-harperdb/#how-to-add-the-socket-io-disconnect-event-listener">Socket.io 接続を切断するイベントリスナーを追加する方法</a></li></ol><h2 id="socket-io-">Socket.io とは</h2><p>Socket.IO を使用することで、サーバー上でイベントが発生したときに、サーバーはリアルタイムで情報をクライアントにプッシュすることができます。</p><p>たとえば、マルチプレイゲームをしているとします。「友達」があなたに対してものすごいゴールを決めてしまうというイベントが発生するかもしれません。</p><p>Socket.IO を使用することで、失点について (ほぼ) 瞬時に知ることができるのです。</p><p>Socket.IO を使用しない場合では、クライアントはサーバーでイベントが発生したかどうかを確認するために複数のポーリング AJAX 呼び出しを行う必要があります。例えば、クライアントは JavaScript を使用して、サーバー上のイベントを 5 秒ごとに確認することも可能でしょう。</p><p>Socket.IO を使用することで、クライアントはサーバー上でイベントが発生したかどうかを確認するために複数のポーリング AJAX 呼び出しを行う必要がなくなります。代わりに、サーバーは情報を取得したら直ちにそれをクライアントに送信します。はるかに効率的ですね。👌</p><p>そのため、Socket.IO を使用すると、チャットアプリやマルチプレイヤーゲームなどのリアルタイムアプリケーションを簡単に構築することができます。</p><h2 id="--1">プロジェクトのセットアップ</h2><h3 id="1-">1. フォルダーの設定方法</h3><p>お好みのテキストエディター (私の場合は VS Code) で新しいプロジェクトを立ち上げ、ルートフォルダーにクライアント (client) とサーバー (server) という 2 つのフォルダーを作成します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/folder-structure.jpeg" class="kg-image" alt="folder-structure" width="371" height="106" loading="lazy"></figure><p>フロントエンド React アプリケーションをクライアントフォルダーに、Node/Express バックエンドをサーバーフォルダーに作成していきます。</p><h3 id="2-">2. クライアントの依存関係をインストールする方法</h3><p>プロジェクトのルートディレクトリでターミナルを開きます。(VS Code では、Ctrl+' を押すか、ターミナル -&gt; 新しいターミナルを選択してください。)</p><p>次に、React をクライアントディレクトリにインストールします。</p><pre><code class="language-bash">$ npx create-react-app client</code></pre><p>React がインストールされたら、ディレクトリをクライアントフォルダーに変更し、次の依存関係をインストールします。</p><pre><code class="language-bash">$ cd client
$ npm i react-router-dom socket.io-client</code></pre><p>React-router-dom を使用することで、さまざまな React コンポーネントへのルートを設定でき、それによって色々なページを作成することができます。</p><p>Socket.io-client は、socket.io のクライアント版であり、イベントをサーバーに「送信」できます。サーバーが受信すると、socket.io のサーバー版を使用して、送信者と同じルーム内のユーザーにメッセージを送信したり、ユーザーをソケットルームに参加させたりすることができます。</p><p>後で実際にコードを使って実践してみると、それが何を意味するのか、もっとわかるかと思います。</p><h3 id="3-react-">3. React アプリの起動方法</h3><p>クライアントディレクトリから次のコマンドを実行して、すべてが順調に稼働していることを確認してみましょう。</p><pre><code class="language-bash">$ npm start</code></pre><p>Webpack は React アプリをビルドし、<a href="http://localhost:3000/">http://localhost:3000</a> で起動します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/react-is-running.jpeg" class="kg-image" alt="react-is-running" width="533" height="400" loading="lazy"></figure><p>次に、ユーザーが送信したメッセージを永続的に保存するために使用する HarperDB データベースを設定しましょう。</p><h3 id="harperdb-">HarperDB のセットアップ方法</h3><p>まず、<a href="https://studio.harperdb.io/">HarperDB でアカウントを作成</a>します。</p><p>次に、新しい HarperDB クラウドインスタンスを作成します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/harper_instance.jpeg" class="kg-image" alt="harper_instance" width="434" height="346" loading="lazy"></figure><p>クラウドインスタンスを選択すると、設定が楽になるのでおすすめです。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/instance-type.jpeg" class="kg-image" alt="instance-type" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/instance-type.jpeg 600w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/instance-type.jpeg 819w" sizes="(min-width: 720px) 720px" width="819" height="398" loading="lazy"></figure><p>クラウドプロバイダーを選択します (私は AWS を選択しました)。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/cloud_provider.jpeg" class="kg-image" alt="cloud_provider" width="491" height="424" loading="lazy"></figure><p>クラウドインスタンスに名前を付け、インスタンスの認証情報を作成します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/instance_credentials.jpeg" class="kg-image" alt="instance_credentials" width="490" height="444" loading="lazy"></figure><p>HarperDB には、有難いことに充分な無料枠があるため、このプロジェクトにはそれを選択します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/instance_specs.jpeg" class="kg-image" alt="instance_specs" width="486" height="661" loading="lazy"></figure><p>全ての詳細が正しいことを確認してから、インスタンスを作成します。</p><p>インスタンスの立ち上げには数分かかるので、早速最初の React コンポーネントを作成してみましょう！</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/instance_loading.jpeg" class="kg-image" alt="instance_loading" width="324" height="208" loading="lazy"></figure><h2 id="--2">「チャットルームに参加する」ページ作成方法</h2><p>私たちのホームページは最終的には次のようになる予定です。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/home-page-1.JPG" class="kg-image" alt="home-page-1" width="475" height="468" loading="lazy"></figure><p>ユーザーはユーザー名を入力し、ドロップダウンからチャットルームを選択して、Join Room (ルームに参加) をクリックします。すると、ユーザーはチャットルームのページへ移動します。</p><p>ということで、早速このホームページを作ってみましょう。</p><h3 id="1-html-">1. HTML フォームの作成とスタイルの追加方法</h3><p>新しいファイル、src/pages/home/index.js を作成します。</p><p>CSS モジュールを使用してアプリに基本的なスタイルを追加するため、新しいファイル src/pages/home/styles.module.css を作成します。</p><p>ディレクトリの構成は次のようになります。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/pages-folder-structure.jpeg" class="kg-image" alt="pages-folder-structure" width="218" height="142" loading="lazy"></figure><p>次に、基本的な HTML のフォームを作成しましょう。</p><pre><code class="language-jsx">// client/src/pages/home/index.js

import styles from './styles.module.css';

const Home = () =&gt; {
  return (
    &lt;div className={styles.container}&gt;
      &lt;div className={styles.formContainer}&gt;
        &lt;h1&gt;{`&lt;&gt;DevRooms&lt;/&gt;`}&lt;/h1&gt;
        &lt;input className={styles.input} placeholder='Username...' /&gt;

        &lt;select className={styles.input}&gt;
          &lt;option&gt;-- Select Room --&lt;/option&gt;
          &lt;option value='javascript'&gt;JavaScript&lt;/option&gt;
          &lt;option value='node'&gt;Node&lt;/option&gt;
          &lt;option value='express'&gt;Express&lt;/option&gt;
          &lt;option value='react'&gt;React&lt;/option&gt;
        &lt;/select&gt;

        &lt;button className='btn btn-secondary'&gt;Join Room&lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Home;</code></pre><p>上では、ユーザー名を取得するための単純なテキスト入力と、ユーザーが参加するチャットルームを選択するためのいくつかのデフォルトオプションを含む select ドロップダウンがあります。</p><p>このコンポーネントを App.js にインポートし、react-router-dom パッケージを使用してコンポーネントのルートを設定します。これはホームページとなるため、パスは単に「/」になります。</p><pre><code class="language-jsx">// client/src/App.js

import './App.css';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './pages/home';

function App() {
  return (
    &lt;Router&gt;
      &lt;div className='App'&gt;
        &lt;Routes&gt;
          &lt;Route path='/' element={&lt;Home /&gt;} /&gt;
        &lt;/Routes&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;</code></pre><p>次に、アプリの見栄えを良くするためにいくつか基本のスタイルを追加していきます。</p><pre><code class="language-css">/* client/src/App.css */

html * {
  font-family: Arial;
  box-sizing: border-box;
}
body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  background: rgb(63, 73, 204);
}
::-webkit-scrollbar {
  width: 20px;
}
::-webkit-scrollbar-track {
  background-color: transparent;
}
::-webkit-scrollbar-thumb {
  background-color: #d6dee1;
  border-radius: 20px;
  border: 6px solid transparent;
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
  background-color: #a8bbbf;
}
.btn {
  padding: 14px 14px;
  border-radius: 6px;
  font-weight: bold;
  font-size: 1.1rem;
  cursor: pointer;
  border: none;
}
.btn-outline {
  color: rgb(153, 217, 234);
  border: 1px solid rgb(153, 217, 234);
  background: rgb(63, 73, 204);
}
.btn-primary {
  background: rgb(153, 217, 234);
  color: rgb(0, 24, 111);
}
.btn-secondary {
  background: rgb(0, 24, 111);
  color: #fff;
}</code></pre><p>ホームページコンポーネントに固有のスタイルも追加していきます。</p><pre><code class="language-css">/* client/src/pages/home/styles.module.css */

.container {
  height: 100vh;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgb(63, 73, 204);
}
.formContainer {
  width: 400px;
  margin: 0 auto 0 auto;
  padding: 32px;
  background: lightblue;
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 28px;
}
.input {
  width: 100%;
  padding: 12px;
  border-radius: 6px;
  border: 1px solid rgb(63, 73, 204);
  font-size: 0.9rem;
}
.input option {
  margin-top: 20px;
}
</code></pre><p>また、スタイル属性を追加して、「ルームに参加」ボタンを全幅にしてみましょう。</p><pre><code class="language-jsx">// client/src/pages/home/index.js

&lt;button className='btn btn-secondary' style={{ width: '100%' }}&gt;Join Room&lt;/button&gt;</code></pre><p>なかなか良くなってきましたね。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/home-page-html.jpeg" class="kg-image" alt="home-page-html" width="468" height="433" loading="lazy"></figure><h3 id="2--1">2. ルーム参加フォームに機能を追加する方法</h3><p>基本的なフォームとスタイルができたので、次に機能を追加して行きたいと思います。</p><p>ユーザーが「ルームに参加」ボタンをクリックしたときに起きて欲しい事項を、以下にまとめました。</p><ol><li>ユーザー名と部屋のフィールドが入力されていることを確認する。</li><li>確認できたら、ソケットイベントをサーバーに送信する。</li><li>ユーザーをチャットページ (後で作成予定) にリダイレクトする。</li></ol><p><em><em>username</em></em> と <em><em>room</em></em> の値を保存する state の作成が必要です。また、ソケットインスタンスの作成も必要です。</p><p>これらの state をホームコンポーネント内で直接作成することもできますが、チャットページもユーザー名、ルーム、ソケットにアクセスする必要があります。そこで、state を App.js に引き上げ、そこでこれらの変数をホームページコンポーネントとチャットページコンポーネントの両方に渡します。</p><p>なのでまず、App.js 内で state の作成とソケットの設定をし、これらの変数を props として &lt;Home /&gt; コンポーネントに渡しましょう。&lt;Home /&gt; から state の値を更新できるように、state の set 関数も渡します。</p><pre><code class="language-jsx">// client/src/App.js

import './App.css';
import { useState } from 'react'; // Add this
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import io from 'socket.io-client'; // Add this
import Home from './pages/home';

const socket = io.connect('http://localhost:4000'); // Add this -- our server will run on port 4000, so we connect to it from here

function App() {
  const [username, setUsername] = useState(''); // Add this
  const [room, setRoom] = useState(''); // Add this

  return (
    &lt;Router&gt;
      &lt;div className='App'&gt;
        &lt;Routes&gt;
          &lt;Route
            path='/'
            element={
              &lt;Home
                username={username} // Add this
                setUsername={setUsername} // Add this
                room={room} // Add this
                setRoom={setRoom} // Add this
                socket={socket} // Add this
              /&gt;
            }
          /&gt;
        &lt;/Routes&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;
</code></pre><p>これで、Home コンポーネント内でもこれらの props にアクセスできるようになりました。以下のように、分割代入 (デストラクチャリング) を使って、props にアクセスします。</p><pre><code class="language-jsx">// client/src/pages/home/index.js

import styles from './style.module.css';

const Home = ({ username, setUsername, room, setRoom, socket }) =&gt; {
  return (
    // ...
  );
};

export default Home;</code></pre><p>ユーザーがユーザー名を入力するか、チャットルームを選択したとき、ユーザー名とルームの state を更新する必要があります。</p><pre><code class="language-jsx">// client/src/pages/home/index.js

// ...

const Home = ({ username, setUsername, room, setRoom, socket }) =&gt; {
  return (
    &lt;div className={styles.container}&gt;
      // ...
        &lt;input
          className={styles.input}
          placeholder='Username...'
          onChange={(e) =&gt; setUsername(e.target.value)} // Add this
        /&gt;

        &lt;select
          className={styles.input}
          onChange={(e) =&gt; setRoom(e.target.value)} // Add this
        &gt;
         // ...
        &lt;/select&gt;

        // ...
    &lt;/div&gt;
  );
};

export default Home;
</code></pre><p>ユーザーが入力したデータを取得しているので、ユーザーが「ルームに参加」ボタンをクリックしたときのコールバック関数、<em>joinRoom()</em> を作成できます。</p><pre><code class="language-jsx">// client/src/pages/home/index.js

// ...

const Home = ({ username, setUsername, room, setRoom, socket }) =&gt; {
  
  // Add this
  const joinRoom = () =&gt; {
    if (room !== '' &amp;&amp; username !== '') {
      socket.emit('join_room', { username, room });
    }
  };

  return (
    &lt;div className={styles.container}&gt;
      // ...
      
        &lt;button
          className='btn btn-secondary'
          style={{ width: '100%' }}
          onClick={joinRoom} // Add this
        &gt;
          Join Room
        &lt;/button&gt;
      // ...
    &lt;/div&gt;
  );
};

export default Home;
</code></pre><p>上では、ユーザーがボタンをクリックすると、ユーザーのユーザーネームと選択したルームを含むオブジェクトとともに、join_room というソケットイベントが発行されます。このイベントはその後サーバーに聞き取りされます。</p><p>ホームページコンポーネントを完成するには、ユーザーを /chat ページに誘導するリダイレクトロジックを joinRoom() 関数の最後に追加する必要があります。</p><pre><code class="language-jsx">// client/src/pages/home/index.js

// ...
import { useNavigate } from 'react-router-dom'; // Add this

const Home = ({ username, setUsername, room, setRoom, socket }) =&gt; {
  const navigate = useNavigate(); // Add this

  const joinRoom = () =&gt; {
    if (room !== '' &amp;&amp; username !== '') {
      socket.emit('join_room', { username, room });
    }

    // Redirect to /chat
    navigate('/chat', { replace: true }); // Add this
  };

 // ...
</code></pre><p>テストしてみましょう: ユーザー名を入力してルームを選択し、[ルームに参加] をクリックします。現在は空のページですが、<a href="http://localhost:3000/chat">http://localhost:3000/chat</a> というルートに転送されるはずです。</p><p>フロントエンドのチャットページを作成する前に、まずは少しサーバーの立ち上げをしてみましょう。</p><h2 id="--3">サーバーの設定方法</h2><p>サーバー側は、イベントリスナーを使ってフロントエンドで発生したソケットイベントの通知を待ちます。現在、React に発生しているイベントは join_room のみなので、まずはこのイベントリスナーを追加します。</p><p>ただその前に、サーバーの依存関係をインストールし、サーバーを起動して実行する必要があります。</p><h3 id="1--1"><strong>1. </strong>サーバーの依存関係をインストールする方法</h3><p>新しいターミナルを開き (VS コード: [ターミナル] -&gt; [新しいターミナル])、​​ディレクトリをサーバーフォルダーに変更し、package.json ファイルを初期化し、次の依存関係をインストールします。</p><pre><code class="language-bash">$ cd server
$ npm init -y
$ npm i axios cors express socket.io dotenv</code></pre><ul><li>Axios は、API へのリクエストを簡易化するために一般的に使用されているパッケージです。</li><li>Cors を使用すると、クライアントは他のオリジンにもリクエストを送ることができます。これは socket.io が適切に作動するために不可欠です。CORS についてまだ聞いたことがない方は、ぜひ「<a href="https://medium.com/@electra_chong/what-is-cors-what-is-it-used-for-308cafa4df1a">CORS とは</a>」を参照してみてください。</li><li>Express は、少ないコードでより簡単にバックエンドを作成できる NodeJS のフレームワークです。</li><li>Socket.io は、標準の HTTP リクエストでは実現不可能な、クライアントとサーバーのリアルタイム通信を可能にするライブラリです。</li><li>Dotenv は、秘密キーとパスワードを安全に保管し、必要に応じてコードに読み込むことができるようにするモジュールです。</li></ul><p>また、nodemon を開発依存関係としてインストールすることで、コードを変更するたびにサーバーを再起動する必要がなく、時間と労力を節約することができます。</p><pre><code class="language-bash">$ npm i -D nodemon</code></pre><h3 id="2--2"><strong>2. </strong>サーバーを起動する方法</h3><p>サーバーディレクトリのルートに index.js というフォルダーを作成し、次のコードを追加してサーバーを起動して実行します。</p><pre><code class="language-javascript">// server/index.js

const express = require('express');
const app = express();
const http = require('http');
const cors = require('cors');

app.use(cors()); // Add cors middleware

const server = http.createServer(app);

server.listen(4000, () =&gt; 'Server is running on port 4000');</code></pre><p>サーバー上の package.json ファイルを開き、開発で nodemon を使用できるようにするスクリプトを追加します。</p><pre><code class="language-json">{
  ...
  "scripts": {
    "dev": "nodemon index.js"
  },
  ...
}
</code></pre><p>次に、下記のコマンドでサーバーを起動しましょう。</p><pre><code class="language-bash">$ npm run dev</code></pre><p>GET リクエストハンドラーを追加することで、サーバーが正しく実行されていることをすぐに確認することができます。</p><pre><code class="language-javascript">// server/index.js

const express = require('express');
const app = express();
http = require('http');
const cors = require('cors');

app.use(cors()); // Add cors middleware

const server = http.createServer(app);

// Add this
app.get('/', (req, res) =&gt; {
  res.send('Hello world');
});

server.listen(4000, () =&gt; 'Server is running on port 3000');</code></pre><p><a href="http://localhost:4000/">http://localhost:4000/</a> に移動します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/localhost4000.jpeg" class="kg-image" alt="localhost4000" width="322" height="186" loading="lazy"></figure><p>サーバーは問題なく稼働しています。今度は、サーバー側の Socket.io の作業を行います。</p><h2 id="-socket-io-">サーバー上で最初の Socket.io イベントリスナーを作成する方法</h2><p>クライアント側から、join_room イベントを送りましたね。これからそのイベント情報をサーバー側で聞き取り、ユーザーをソケットルームに追加していきます。</p><p>しかしまず、クライアントが Socket.io のクライアント経由でサーバーに接続したかを確認する必要があります。</p><pre><code class="language-javascript">// server/index.js

const express = require('express');
const app = express();
http = require('http');
const cors = require('cors');
const { Server } = require('socket.io'); // Add this

app.use(cors()); // Add cors middleware

const server = http.createServer(app); // Add this

// Add this
// Create an io server and allow for CORS from http://localhost:3000 with GET and POST methods
const io = new Server(server, {
  cors: {
    origin: 'http://localhost:3000',
    methods: ['GET', 'POST'],
  },
});

// Add this
// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
  console.log(`User connected ${socket.id}`);

  // We can write our socket event listeners in here...
});

server.listen(4000, () =&gt; 'Server is running on port 3000');</code></pre><p>これで、クライアントがフロントエンドから接続すると、バックエンドが接続イベントをキャプチャし、その特定のクライアントの一意のソケット ID を使用して <code>User connected</code> とログに記録します。</p><p>一度、サーバーがクライアントからの接続イベントをキャプチャしているかどうかをテストしてみましょう。<a href="http://localhost:3000/">http://localhost:3000/</a> にある React アプリに移動し、ページを更新してみてください。</p><p>サーバー端末コンソールに次のログが表示されているはずです。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/user-connected.jpeg" class="kg-image" alt="user-connected" width="442" height="93" loading="lazy"></figure><p>見事、クライアントが socket.io 経由でサーバーに接続しました。これでクライアントとサーバーがリアルタイムで通信できるようになりました。</p><h2 id="socket-io--1">Socket.io のルームの仕組み</h2><p><a href="https://socket.io/docs/v3/rooms/">Socket.io ドキュメント</a>より:</p><blockquote>「ルームとは、ソケットが出入り (<code>join</code> と <code>leave</code>) できる任意のチャネルです。クライアントのサブセットにイベントをブロードキャストするために使用できます。」</blockquote><p>したがって、ユーザーをルームに参加させると、サーバーはそのルーム内のすべてのユーザーにメッセージを送信できるため、ユーザーはリアルタイムで相互にメッセージを送信できるようになるのです。素晴らしい！</p><h3 id="-socket-io--1">ユーザーを Socket.io ルームに参加させる方法</h3><p>ユーザーが Socket.io を介して接続したら、クライアントから通知されたイベントを聞き取るために、サーバー上でソケットイベントリスナーを追加することができます。また、サーバー上でイベント情報を発行し、クライアント上で聞き取ることもできます。</p><p>早速 join_room イベントの発生を待ち、データ (ユーザー名とルーム) をキャプチャして、ユーザーをソケットルームに追加しましょう。</p><pre><code class="language-javascript">// server/index.js

// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
  console.log(`User connected ${socket.id}`);

  // Add this
  // Add a user to a room
  socket.on('join_room', (data) =&gt; {
    const { username, room } = data; // Data sent from client when join_room event emitted
    socket.join(room); // Join the user to a socket room
  });
});</code></pre><h3 id="--4">ルーム内のユーザーにメッセージを送信する方法</h3><p>まずは、参加したばかりのユーザー以外のルーム内すべてのユーザーにメッセージを送信して、新しいユーザーが参加したことを通知しましょう。</p><pre><code class="language-javascript">// server/index.js

const CHAT_BOT = 'ChatBot'; // Add this
// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
  console.log(`User connected ${socket.id}`);

  // Add a user to a room
  socket.on('join_room', (data) =&gt; {
    const { username, room } = data; // Data sent from client when join_room event emitted
    socket.join(room); // Join the user to a socket room

    // Add this
    let __createdtime__ = Date.now(); // Current timestamp
    // Send message to all users currently in the room, apart from the user that just joined
    socket.to(room).emit('receive_message', {
      message: `${username} has joined the chat room`,
      username: CHAT_BOT,
      __createdtime__,
    });
  });
});</code></pre><p>上記では、現在のユーザーが参加したばかりのルーム内にいる全クライアントに、receive_message イベントを発行しています。そしてそのイベントには、メッセージ本文、メッセージを送信したユーザー名、メッセージの送信時刻などのデータが含まれています。</p><p>少し後で React アプリケーションにイベントリスナーを追加して、このイベントをキャプチャし、画面にメッセージを出力します。</p><p>新しく参加したユーザーにも、ウェルカムメッセージを送信しましょう。</p><pre><code class="language-javascript">// server/index.js

io.on('connection', (socket) =&gt; {
  // ...

    // Add this
    // Send welcome msg to user that just joined chat only
    socket.emit('receive_message', {
      message: `Welcome ${username}`,
      username: CHAT_BOT,
      __createdtime__,
    });
  });
});</code></pre><p>ユーザーを Socket.io のルームに追加すると、Socket.io は各ユーザーのソケット ID のみを保存します。しかし今後、ルーム名だけでなく、ルームにいる全員のユーザー名も必要になってきます。そこで、これらのデータをサーバー上の変数に保存しておきましょう。</p><pre><code class="language-javascript">// server/index.js

// ...

const CHAT_BOT = 'ChatBot';
// Add this
let chatRoom = ''; // E.g. javascript, node,...
let allUsers = []; // All users in current chat room

// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
    // ...
    
    // Add this
    // Save the new user to the room
    chatRoom = room;
    allUsers.push({ id: socket.id, username, room });
    chatRoomUsers = allUsers.filter((user) =&gt; user.room === room);
    socket.to(room).emit('chatroom_users', chatRoomUsers);
    socket.emit('chatroom_users', chatRoomUsers);
  });
});</code></pre><p>上記では、chatroom_users イベントを介して全ユーザー (chatRoomUsers) の配列もクライアントに送り返しています。それによって、フロントエンドでルーム内のすべてのユーザー名をリストすることができるようになります。</p><p>サーバーにもっとコードを追加する前に、フロントエンドに戻ってチャットページを作成しましょう。そうすることによって、receive_message イベントをフロントエンドできちんと受信できて​​いるかどうかをテストすることができます。</p><h2 id="--5">チャットページを作成する方法</h2><p>クライアントフォルダーに、2 つの新しいファイルを作成します。</p><ol><li>src/pages/chat/index.js</li><li>src/pages/chat/styles.module.css</li></ol><p>まずはチャットページとコンポーネントで使用するスタイルをいくつか追加しましょう。</p><pre><code class="language-css">/* client/src/pages/chat/styles.module.css */

.chatContainer {
  max-width: 1100px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 4fr;
  gap: 20px;
}

/* Room and users component */
.roomAndUsersColumn {
  border-right: 1px solid #dfdfdf;
}
.roomTitle {
  margin-bottom: 60px;
  text-transform: uppercase;
  font-size: 2rem;
  color: #fff;
}
.usersTitle {
  font-size: 1.2rem;
  color: #fff;
}
.usersList {
  list-style-type: none;
  padding-left: 0;
  margin-bottom: 60px;
  color: rgb(153, 217, 234);
}
.usersList li {
  margin-bottom: 12px;
}

/* Messages */
.messagesColumn {
  height: 85vh;
  overflow: auto;
  padding: 10px 10px 10px 40px;
}
.message {
  background: rgb(0, 24, 111);
  border-radius: 6px;
  margin-bottom: 24px;
  max-width: 600px;
  padding: 12px;
}
.msgMeta {
  color: rgb(153, 217, 234);
  font-size: 0.75rem;
}
.msgText {
  color: #fff;
}

/* Message input and button */
.sendMessageContainer {
  padding: 16px 20px 20px 16px;
}
.messageInput {
  padding: 14px;
  margin-right: 16px;
  width: 60%;
  border-radius: 6px;
  border: 1px solid rgb(153, 217, 234);
  font-size: 0.9rem;
}
</code></pre><p>追加できたら、チャットページが下のようになるかを見てみましょう。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/chat-page.jpeg" class="kg-image" alt="chat-page" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/chat-page.jpeg 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/chat-page.jpeg 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/chat-page.jpeg 1216w" sizes="(min-width: 720px) 720px" width="1216" height="678" loading="lazy"></figure><p>このページのすべてのコードとロジックを 1 つのファイルにまとめてしまうと、読みにくく管理が難しくなってしまうため、せっかく便利なフロントエンドフレームワーク (React) を使用しているので、<strong>このページをコンポーネントに分けます</strong>。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-248.png" class="kg-image" alt="image-248" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-248.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/image-248.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-248.png 1232w" sizes="(min-width: 720px) 720px" width="1232" height="719" loading="lazy"></figure><h3 id="--6">チャットページのコンポーネント:</h3><p><strong><strong>A</strong></strong>: チャットルーム名、そのチャットルーム内にいるユーザーリスト、およびユーザーをチャットルームから削除する「退出」ボタンが含まれる部分。</p><p><strong><strong>B</strong></strong>: 送信されたメッセージ。最初のレンダリング時に、そのチャットルームで送信された最新 100 件のメッセージがデータベースから取得され、ユーザーに表示されます。</p><p><strong><strong>C</strong></strong>: メッセージを入力して送信するための入力フィールドとボタン。</p><p>まずはコンポーネント B を作成して、ユーザーにメッセージを表示できるようにします。</p><h2 id="-b-">メッセージコンポーネント (B) を作成する方法</h2><p>新しいファイル src/pages/chat/messages.js を作成し、次のコードを追加してください。</p><pre><code class="language-jsx">// client/src/pages/chat/messages.js

import styles from './styles.module.css';
import { useState, useEffect } from 'react';

const Messages = ({ socket }) =&gt; {
  const [messagesRecieved, setMessagesReceived] = useState([]);

  // Runs whenever a socket event is recieved from the server
  useEffect(() =&gt; {
    socket.on('receive_message', (data) =&gt; {
      console.log(data);
      setMessagesReceived((state) =&gt; [
        ...state,
        {
          message: data.message,
          username: data.username,
          __createdtime__: data.__createdtime__,
        },
      ]);
    });

	// Remove event listener on component unmount
    return () =&gt; socket.off('receive_message');
  }, [socket]);

  // dd/mm/yyyy, hh:mm:ss
  function formatDateFromTimestamp(timestamp) {
    const date = new Date(timestamp);
    return date.toLocaleString();
  }

  return (
    &lt;div className={styles.messagesColumn}&gt;
      {messagesRecieved.map((msg, i) =&gt; (
        &lt;div className={styles.message} key={i}&gt;
          &lt;div style={{ display: 'flex', justifyContent: 'space-between' }}&gt;
            &lt;span className={styles.msgMeta}&gt;{msg.username}&lt;/span&gt;
            &lt;span className={styles.msgMeta}&gt;
              {formatDateFromTimestamp(msg.__createdtime__)}
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;p className={styles.msgText}&gt;{msg.message}&lt;/p&gt;
          &lt;br /&gt;
        &lt;/div&gt;
      ))}
    &lt;/div&gt;
  );
};

export default Messages;</code></pre><p>上記には、ソケットイベントが受信されるたびに実行される <em>useEffect</em> フックがあります。次に、receive_message というイベントリスナーに渡されたメッセージデータを取得します。そこから、<em>messagesReceived</em> の state を設定します。これは、メッセージ、送信者のユーザー名、メッセージが送信された日付を含むメッセージオブジェクトの配列です。</p><p>今作ったこのメッセージコンポーネントをチャットページにインポートし、App.js でチャットページのルートを作成しましょう。</p><pre><code class="language-jsx">// client/src/pages/chat/index.js

import styles from './styles.module.css';
import MessagesReceived from './messages';

const Chat = ({ socket }) =&gt; {
  return (
    &lt;div className={styles.chatContainer}&gt;
      &lt;div&gt;
        &lt;MessagesReceived socket={socket} /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Chat;
</code></pre><pre><code class="language-jsx">// client/src/App.js

import './App.css';
import { useState } from 'react';
import Home from './pages/home';
import Chat from './pages/chat';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import io from 'socket.io-client';

const socket = io.connect('http://localhost:4000');

function App() {
  const [username, setUsername] = useState('');
  const [room, setRoom] = useState('');

  return (
    &lt;Router&gt;
      &lt;div className='App'&gt;
        &lt;Routes&gt;
          &lt;Route
            path='/'
            element={
              &lt;Home
                username={username}
                setUsername={setUsername}
                room={room}
                setRoom={setRoom}
                socket={socket}
              /&gt;
            }
          /&gt;
          {/* Add this */}
          &lt;Route
            path='/chat'
            element={&lt;Chat username={username} room={room} socket={socket} /&gt;}
          /&gt;
        &lt;/Routes&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;
</code></pre><p>一度テストしてみましょう。ホームページにアクセスしてルームに参加してみてください。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/joining-a-room.jpeg" class="kg-image" alt="joining-a-room" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/joining-a-room.jpeg 600w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/joining-a-room.jpeg 661w" width="661" height="538" loading="lazy"></figure><p>チャットページに移動し、ChatBot からウェルカムメッセージを受け取ります。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/welcome-message.jpeg" class="kg-image" alt="welcome-message" width="571" height="357" loading="lazy"></figure><p>ユーザーは受信したメッセージを確認できるようになりました。上出来です！</p><p>次は、メッセージを永続的に保存できるようにデータベースを設定します。</p><h2 id="harperdb--1">HarperDB でスキーマとテーブルを作成する方法</h2><p>HarperDB のダッシュボードに戻り、「browse (参照)」をクリックします。次に、realtime_chat_app という新しいスキーマを作成します。スキーマというのは、単に複数のテーブルをまとめたもののことです。</p><p>そのスキーマ内に、ハッシュ属性が id の messages というテーブルを作成します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-258.png" class="kg-image" alt="image-258" width="591" height="430" loading="lazy"></figure><p>これで、メッセージを保存する場所ができたので、SendMessage コンポーネントを作成しましょう。</p><h2 id="-c-">メッセージ送信コンポーネント (C) の作成方法</h2><p>ファイル src/pages/chat/send-message.js を作成し、次のコードを追加してください。</p><pre><code class="language-jsx">// client/src/pages/chat/send-message.js

import styles from './styles.module.css';
import React, { useState } from 'react';

const SendMessage = ({ socket, username, room }) =&gt; {
  const [message, setMessage] = useState('');

  const sendMessage = () =&gt; {
    if (message !== '') {
      const __createdtime__ = Date.now();
      // Send message to server. We can't specify who we send the message to from the frontend. We can only send to server. Server can then send message to rest of users in room
      socket.emit('send_message', { username, room, message, __createdtime__ });
      setMessage('');
    }
  };

  return (
    &lt;div className={styles.sendMessageContainer}&gt;
      &lt;input
        className={styles.messageInput}
        placeholder='Message...'
        onChange={(e) =&gt; setMessage(e.target.value)}
        value={message}
      /&gt;
      &lt;button className='btn btn-primary' onClick={sendMessage}&gt;
        Send Message
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default SendMessage;</code></pre><p>上では、ユーザーが「メッセージの送信」というボタンをクリックすると、send_message のソケットイベントがメッセージオブジェクトとともにサーバーに送信されます。後ほどこのイベントをサーバー上で処理します。</p><p>SendMessage をチャットページにインポートします。</p><pre><code class="language-js">// src/pages/chat/index.js

import styles from './styles.module.css';
import MessagesReceived from './messages';
import SendMessage from './send-message';

const Chat = ({ username, room, socket }) =&gt; {
  return (
    &lt;div className={styles.chatContainer}&gt;
      &lt;div&gt;
        &lt;MessagesReceived socket={socket} /&gt;
        &lt;SendMessage socket={socket} username={username} room={room} /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Chat;</code></pre><p>チャットページは次のようになります。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-259.png" class="kg-image" alt="image-259" width="521" height="980" loading="lazy"></figure><p>次に、データベースとのやり取りを開始できるように、HarperDB の環境変数を設定する必要があります。</p><h2 id="harperdb--2">HarperDB の環境変数を設定する方法</h2><p>HarperDB にメッセージを保存できるようにするには、HarperDB インスタンスの URL と API パスワードが必要です。</p><p>HarperDB ダッシュボードでインスタンスをクリックし、「config (設定)」に移動します。インスタンス URL とインスタンス API 認証ヘッダー (データベースへのあらゆるリクエストを許可する「super_user」パスワード) が表示されます。絶対に誰とも共有しないでください！</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-263.png" class="kg-image" alt="image-263" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-263.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-263.png 943w" sizes="(min-width: 720px) 720px" width="943" height="410" loading="lazy"></figure><p>これらの変数を .env ファイルに保存します。<strong>警告: .env ファイルは絶対に GitHub にプッシュしないでください</strong>。このファイルは一般に公開すべきものではありません。これらの変数はバックグラウンドでサーバー経由で読み込みされます。</p><p>次のファイルを作成し、あなたの HarperDB の URL とパスワードを追加します。</p><pre><code class="language-bash">// server/.env

HARPERDB_URL="&lt;your url goes here&gt;"
HARPERDB_PW="Basic &lt;your password here&gt;"</code></pre><p>また、.env が node_modules フォルダーとともに GitHub にプッシュされるのを防ぐための .gitignore ファイルも作成します。</p><pre><code class="language-bash">// server/.gitignore

.env
node_modules</code></pre><p>注意: Git や GitHub をうまく使いこなすことは全ての開発者にとって必須です。もし Git の流儀がまだ曖昧な方は、私の <a href="https://www.doabledanny.com/git-workflows">Git のワークフローに関する記事</a>を見てみてください。</p><p>または、同じ Git コマンドをいつも検索している方や、コマンドを検索、修正、コピー/ ペーストする簡単な方法をお探しの方はぜひ、私が作った人気 <a href="https://doabledanny.gumroad.com/l/git-commands-cheat-sheet-pdf">Git コマンドチートシートの PDF 版</a>と <a href="https://doabledanny.gumroad.com/l/git-cheat-sheet-poster">Git チートシートポスター版</a>をチェックしてみてください。</p><p>最後に、次のコードをメインサーバーファイルの先頭に追加して、環境変数をサーバーに読み込みましょう。</p><pre><code class="language-js">// server/index.js

require('dotenv').config();
console.log(process.env.HARPERDB_URL); // remove this after you've confirmed it working
const express = require('express');
// ...</code></pre><h2 id="socket-io--2">Socket.io を利用してユーザーが相互にメッセージを送信できるようにする方法</h2><p>サーバー上で send_message のイベントを聞き取り、ルーム内のすべてのユーザーにメッセージを送信します。</p><pre><code class="language-js">// server/index.js

const express = require('express');
// ...
const harperSaveMessage = require('./services/harper-save-message'); // Add this

// ...

// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
    
  // ...

  // Add this
  socket.on('send_message', (data) =&gt; {
    const { message, username, room, __createdtime__ } = data;
    io.in(room).emit('receive_message', data); // Send to all users in room, including sender
    harperSaveMessage(message, username, room, __createdtime__) // Save message in db
      .then((response) =&gt; console.log(response))
      .catch((err) =&gt; console.log(err));
  });
});

server.listen(4000, () =&gt; 'Server is running on port 3000');
</code></pre><p>次に、harperSaveMessage という関数を作成する必要があります。server/services/harper-save-message.js に新しいファイルを作成し、以下のコードを追加します。</p><pre><code class="language-js">// server/services/harper-save-message.js

var axios = require('axios');

function harperSaveMessage(message, username, room) {
  const dbUrl = process.env.HARPERDB_URL;
  const dbPw = process.env.HARPERDB_PW;
  if (!dbUrl || !dbPw) return null;

  var data = JSON.stringify({
    operation: 'insert',
    schema: 'realtime_chat_app',
    table: 'messages',
    records: [
      {
        message,
        username,
        room,
      },
    ],
  });

  var config = {
    method: 'post',
    url: dbUrl,
    headers: {
      'Content-Type': 'application/json',
      Authorization: dbPw,
    },
    data: data,
  };

  return new Promise((resolve, reject) =&gt; {
    axios(config)
      .then(function (response) {
        resolve(JSON.stringify(response.data));
      })
      .catch(function (error) {
        reject(error);
      });
  });
}

module.exports = harperSaveMessage;
</code></pre><p>上記では、データの保存には少し時間がかかる場合があるため、データが正常に保存された場合は解決され、失敗した場合は拒否される Promise を返しています。</p><p>上記のコードをどこで入手したか気になりましたか？HarperDB のスタジオダッシュボードには、便利な「<a href="https://studio.harperdb.io/resources/examples/QuickStart%20Examples/Create%20dev%20Schema">コードサンプル</a>」というセクションが用意されており、それを使うことによって作業がはるかに簡単になります。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-265.png" class="kg-image" alt="image-265" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-265.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/image-265.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-265.png 1113w" sizes="(min-width: 720px) 720px" width="1113" height="713" loading="lazy"></figure><p>テストの時間です！ユーザーとしてルームに参加し、メッセージを送信します。次に、HarperDB に移動し、「browse (参照)」をクリックして、「メッセージ」テーブルをクリックします。データベースにメッセージが表示されているはずです。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-264.png" class="kg-image" alt="image-264" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-264.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-264.png 937w" sizes="(min-width: 720px) 720px" width="937" height="383" loading="lazy"></figure><p>素晴らしい😎これができたら次は、ユーザーがチャットルームに参加したときに、ルーム内で送信された最新 100 件のメッセージが読み込まれるようになれば最高だと思いませんか？</p><h2 id="harperdb--3">HarperDB からメッセージを取得する方法</h2><p>サーバー上で、特定のチャットルームで送信された最新 100 件のメッセージを取得する関数を作成しましょう (HarperDB では SQL クエリも使用できるんですよ👌):</p><pre><code class="language-js">// server/services/harper-get-messages.js

let axios = require('axios');

function harperGetMessages(room) {
  const dbUrl = process.env.HARPERDB_URL;
  const dbPw = process.env.HARPERDB_PW;
  if (!dbUrl || !dbPw) return null;

  let data = JSON.stringify({
    operation: 'sql',
    sql: `SELECT * FROM realtime_chat_app.messages WHERE room = '${room}' LIMIT 100`,
  });

  let config = {
    method: 'post',
    url: dbUrl,
    headers: {
      'Content-Type': 'application/json',
      Authorization: dbPw,
    },
    data: data,
  };

  return new Promise((resolve, reject) =&gt; {
    axios(config)
      .then(function (response) {
        resolve(JSON.stringify(response.data));
      })
      .catch(function (error) {
        reject(error);
      });
  });
}

module.exports = harperGetMessages;</code></pre><p>ユーザーがチャットルームに参加するたびにこの関数を呼び出します。</p><pre><code class="language-js">// server/index.js

// ...
const harperSaveMessage = require('./services/harper-save-message');
const harperGetMessages = require('./services/harper-get-messages'); // Add this

// ...

// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
  console.log(`User connected ${socket.id}`);

  // Add a user to a room
  socket.on('join_room', (data) =&gt; {
      
    // ...

    // Add this
    // Get last 100 messages sent in the chat room
    harperGetMessages(room)
      .then((last100Messages) =&gt; {
        // console.log('latest messages', last100Messages);
        socket.emit('last_100_messages', last100Messages);
      })
      .catch((err) =&gt; console.log(err));
  });

 // ...</code></pre><p>上記では、メッセージが正常にフェッチされると、last_100_messages という Socket.io イベントが発行されます。そうすると、フロントエンドでこのイベントを聞き取ります。</p><h2 id="-100-">クライアントで最新メッセージ 100 件を表示する方法</h2><p>以下では、<em>last_100_messages</em> イベントのための、Socket.io イベントリスナーを含む useEffect フックを追加します。そこから、メッセージは日付順に並べ替えられ、最新のメッセージが一番下に表示され、<em>messagesReceived</em> の state が更新されます。</p><p><em>messagesReceived</em> が更新されると、useEffect が実行され、<em>messageColumn</em> div が最新のメッセージまでスクロールされます。これにより、アプリのユーザーエクスペリエンスが向上します👍。</p><pre><code class="language-js">// client/src/pages/chat/messages.js

import styles from './styles.module.css';
import { useState, useEffect, useRef } from 'react';

const Messages = ({ socket }) =&gt; {
  const [messagesRecieved, setMessagesReceived] = useState([]);

  const messagesColumnRef = useRef(null); // Add this

  // Runs whenever a socket event is recieved from the server
  useEffect(() =&gt; {
    socket.on('receive_message', (data) =&gt; {
      console.log(data);
      setMessagesReceived((state) =&gt; [
        ...state,
        {
          message: data.message,
          username: data.username,
          __createdtime__: data.__createdtime__,
        },
      ]);
    });

    // Remove event listener on component unmount
    return () =&gt; socket.off('receive_message');
  }, [socket]);

  // Add this
  useEffect(() =&gt; {
    // Last 100 messages sent in the chat room (fetched from the db in backend)
    socket.on('last_100_messages', (last100Messages) =&gt; {
      console.log('Last 100 messages:', JSON.parse(last100Messages));
      last100Messages = JSON.parse(last100Messages);
      // Sort these messages by __createdtime__
      last100Messages = sortMessagesByDate(last100Messages);
      setMessagesReceived((state) =&gt; [...last100Messages, ...state]);
    });

    return () =&gt; socket.off('last_100_messages');
  }, [socket]);

  // Add this
  // Scroll to the most recent message
  useEffect(() =&gt; {
    messagesColumnRef.current.scrollTop =
      messagesColumnRef.current.scrollHeight;
  }, [messagesRecieved]);

  // Add this
  function sortMessagesByDate(messages) {
    return messages.sort(
      (a, b) =&gt; parseInt(a.__createdtime__) - parseInt(b.__createdtime__)
    );
  }

  // dd/mm/yyyy, hh:mm:ss
  function formatDateFromTimestamp(timestamp) {
    const date = new Date(timestamp);
    return date.toLocaleString();
  }

  return (
    // Add ref to this div
    &lt;div className={styles.messagesColumn} ref={messagesColumnRef}&gt;
      {messagesRecieved.map((msg, i) =&gt; (
        &lt;div className={styles.message} key={i}&gt;
          &lt;div style={{ display: 'flex', justifyContent: 'space-between' }}&gt;
            &lt;span className={styles.msgMeta}&gt;{msg.username}&lt;/span&gt;
            &lt;span className={styles.msgMeta}&gt;
              {formatDateFromTimestamp(msg.__createdtime__)}
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;p className={styles.msgText}&gt;{msg.message}&lt;/p&gt;
          &lt;br /&gt;
        &lt;/div&gt;
      ))}
    &lt;/div&gt;
  );
};

export default Messages;
</code></pre><h2 id="-a-">チャットルームとユーザー (A) の表示方法</h2><p>コンポーネント B と C の作成が完了したので、最後に A を作成しましょう。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-248-1.png" class="kg-image" alt="image-248-1" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-248-1.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/image-248-1.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-248-1.png 1232w" sizes="(min-width: 720px) 720px" width="1232" height="719" loading="lazy"></figure><p>サーバー上では、誰かユーザーがチャットルームに参加すると、ルーム内全ユーザーをそのルーム内の全クライアントに送る<em> chatroom_users</em> イベントが発行されます。<em>RoomAndUsers</em> というコンポーネントでそのイベントの聞き取りをしてみましょう。</p><p>下のコードには、「Leave (退室)」ボタンもあり、これを押すとサーバーに <em>leave_room</em> イベントが発行されます。その後、そのユーザーをホームページへリダイレクトします。</p><pre><code class="language-js">// client/src/pages/chat/room-and-users.js

import styles from './styles.module.css';
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

const RoomAndUsers = ({ socket, username, room }) =&gt; {
  const [roomUsers, setRoomUsers] = useState([]);

  const navigate = useNavigate();

  useEffect(() =&gt; {
    socket.on('chatroom_users', (data) =&gt; {
      console.log(data);
      setRoomUsers(data);
    });

    return () =&gt; socket.off('chatroom_users');
  }, [socket]);

  const leaveRoom = () =&gt; {
    const __createdtime__ = Date.now();
    socket.emit('leave_room', { username, room, __createdtime__ });
    // Redirect to home page
    navigate('/', { replace: true });
  };

  return (
    &lt;div className={styles.roomAndUsersColumn}&gt;
      &lt;h2 className={styles.roomTitle}&gt;{room}&lt;/h2&gt;

      &lt;div&gt;
        {roomUsers.length &gt; 0 &amp;&amp; &lt;h5 className={styles.usersTitle}&gt;Users:&lt;/h5&gt;}
        &lt;ul className={styles.usersList}&gt;
          {roomUsers.map((user) =&gt; (
            &lt;li
              style={{
                fontWeight: `${user.username === username ? 'bold' : 'normal'}`,
              }}
              key={user.id}
            &gt;
              {user.username}
            &lt;/li&gt;
          ))}
        &lt;/ul&gt;
      &lt;/div&gt;

      &lt;button className='btn btn-outline' onClick={leaveRoom}&gt;
        Leave
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default RoomAndUsers;</code></pre><p>このコンポーネントをチャットページにインポートしましょう。</p><pre><code class="language-js">// client/src/pages/chat/index.js

import styles from './styles.module.css';
import RoomAndUsersColumn from './room-and-users'; // Add this
import SendMessage from './send-message';
import MessagesReceived from './messages';

const Chat = ({ username, room, socket }) =&gt; {
  return (
    &lt;div className={styles.chatContainer}&gt;
      {/* Add this */}
      &lt;RoomAndUsersColumn socket={socket} username={username} room={room} /&gt;

      &lt;div&gt;
        &lt;MessagesReceived socket={socket} /&gt;
        &lt;SendMessage socket={socket} username={username} room={room} /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Chat;
</code></pre><h2 id="socket-io--3">Socket.io のルームからユーザーを削除する方法</h2><p>Socket.io には、Socket.io ルームからユーザーを削除するために使用できる <em>leave()</em> メソッドが用意されています。また、サーバーメモリー上の配列でユーザー情報を管理しているため、この配列からもユーザーを削除します。</p><pre><code class="language-js">// server/index.js

const leaveRoom = require('./utils/leave-room'); // Add this

// ...

// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
    
  // ...

  // Add this
  socket.on('leave_room', (data) =&gt; {
    const { username, room } = data;
    socket.leave(room);
    const __createdtime__ = Date.now();
    // Remove user from memory
    allUsers = leaveRoom(socket.id, allUsers);
    socket.to(room).emit('chatroom_users', allUsers);
    socket.to(room).emit('receive_message', {
      username: CHAT_BOT,
      message: `${username} has left the chat`,
      __createdtime__,
    });
    console.log(`${username} has left the chat`);
  });
});

server.listen(4000, () =&gt; 'Server is running on port 3000');
</code></pre><p>次に、<em>leaveRoom()</em> という関数を作成する必要があります。</p><pre><code class="language-js">// server/utils/leave-room.js

function leaveRoom(userID, chatRoomUsers) {
  return chatRoomUsers.filter((user) =&gt; user.id != userID);
}

module.exports = leaveRoom;
</code></pre><p>なぜこの短い関数を別の utils フォルダーに置いておく必要があるのでしょうか。それは、後で再び使用することになるので、同じことを繰り返さなくてもいいようにです。(コードを <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> に保つことができます。)</p><p>さあ、テストしてみましょう。2 つのブラウザーを並べて開き、両方のブラウザーでチャットに参加してみてください。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-266.png" class="kg-image" alt="image-266" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-266.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/image-266.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-266.png 1441w" sizes="(min-width: 720px) 720px" width="1441" height="954" loading="lazy"></figure><p>次に、ブラウザー 2 の Leave (退室) ボタンをクリックします。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-267.png" class="kg-image" alt="image-267" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2024/02/image-267.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2024/02/image-267.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2024/02/image-267.png 1341w" sizes="(min-width: 720px) 720px" width="1341" height="952" loading="lazy"></figure><p>ユーザーはチャットから削除され、他のユーザーに、退席したことを通知するメッセージが送信されます。ナイス！</p><h2 id="socket-io--4">Socket.io 接続を切断するイベントリスナーを追加する方法</h2><p>インターネットの接続が切断された場合など、ユーザーが何らかの理由でサーバーから切断された場合はどうなるのでしょうか？Socket.io にはそのような時のために、ビルトインの切断イベントリスナーがあります。これをサーバーに追加して、接続の切断時にユーザーをメモリから削除します。</p><pre><code class="language-js">// server/index.js

// ...

// Listen for when the client connects via socket.io-client
io.on('connection', (socket) =&gt; {
    
  // ...
    
  // Add this
  socket.on('disconnect', () =&gt; {
    console.log('User disconnected from the chat');
    const user = allUsers.find((user) =&gt; user.id == socket.id);
    if (user?.username) {
      allUsers = leaveRoom(socket.id, allUsers);
      socket.to(chatRoom).emit('chatroom_users', allUsers);
      socket.to(chatRoom).emit('receive_message', {
        message: `${user.username} has disconnected from the chat.`,
      });
    }
  });
});

server.listen(4000, () =&gt; 'Server is running on port 3000');
</code></pre><p>これで、React フロントエンド、Node/Express バックエンド、HarperDB データベースを備えたフルスタックのリアルタイムチャットアプリケーションが構築されました。お見事！</p><p>次回は、ユーザーが HarperDB 内で独自の API エンドポイントを定義できるようにする HarperDB の<a href="https://harperdb.io/docs/custom-functions/">カスタム関数</a>をご紹介する予定です。こうすることで、アプリケーション全体を 1 か所でまとめて構築できます。HarperDB でスタックを縮小 (シンプル化) する例は、<a href="https://harperdb.io/blog/mean-stack-alternative/">この記事で</a>ご覧ください。</p><h2 id="--7"><strong>挑戦状💪</strong></h2><p>チャットページでページを更新すると、ユーザーのユーザーネームとルームが失われてしまいます。ユーザーがページを更新してもこの情報が失われないようにする方法を探し出してみてください。ヒント: <a href="https://www.w3schools.com/html/html5_webstorage.asp">ローカルストレージ</a>が役立つかもしれません！</p><h2 id="--8"><strong><strong><strong>最後まで読んでいただきありがとうございます！</strong></strong></strong></h2><p>この記事が少しでもお役に立てたのであれば、ぜひ以下もお願いします。</p><ul><li><a href="https://www.youtube.com/channel/UC0URylW_U4i26wN231yRqvA">私の YouTube チャンネルに登録</a>。React/NextJS/Node/Express に関する詳細なチュートリアルとプロジェクトビデオを投稿していく予定です。</li><li><a href="https://twitter.com/doabledanny">Twitter でフォロー</a>。私のフリーランス活動、趣味のプロジェクトや、最近学んでいることについてツイートしています。</li><li><a href="https://doabledanny.gumroad.com/">私の Gumroad ストアをチェックする</a>。そこでは、便利で人気な早見表やポスターを作成していて、執筆現在までに 8000 ダウンロードを達成しています。</li><li><a href="https://www.doabledanny.com/blog/">私の Web 開発ブログをチェックする。</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Python Do While – ループの例 ]]>
                </title>
                <description>
                    <![CDATA[ ループは、すべてのモダンなプログラミング言語で頻繁に使用されている便利な機能です。 特定の反復的なタスクを自動化したい場合や、プログラム内で反復的なコードを書くのを避けたい場合は、ループを使用するのが最適です。 ループは、条件が満たされるまで繰り返し実行される一連の指示です。Python でループがどのように機能するかについて詳しく学びましょう。 Python におけるループ Python には 2 種類のループが組み込まれています。  * for ループ  * while ループ Python で while ループを作成する方法とその仕組みに焦点を当てましょう。 Python における while ループとは何か Python の while ループの一般的な構文は次のようになります。 while condition:     execute this code in the loop's body while ループは、条件が True の間コードを実行します。その条件が True でなくなるまで、必要なコード文のセットを実行し続けます。 while ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/python-do-while-loop-example/</link>
                <guid isPermaLink="false">656511608d146503e8e3e58e</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Saki Basken ]]>
                </dc:creator>
                <pubDate>Wed, 06 Dec 2023 11:54:32 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2023/11/pexels-pixabay-106155.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/python-do-while-loop-example/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Python Do While – Loop Example</a>
      </p><p>ループは、すべてのモダンなプログラミング言語で頻繁に使用されている便利な機能です。</p><p>特定の反復的なタスクを自動化したい場合や、プログラム内で反復的なコードを書くのを避けたい場合は、ループを使用するのが最適です。</p><p>ループは、条件が満たされるまで繰り返し実行される一連の指示です。Python でループがどのように機能するかについて詳しく学びましょう。</p><h2 id="python-">Python におけるループ</h2><p>Python には 2 種類のループが組み込まれています。</p><ul><li><code>for</code> ループ</li><li><code>while</code> ループ</li></ul><p>Python で <code>while</code> ループを作成する方法とその仕組みに焦点を当てましょう。</p><h2 id="python-while-">Python における while ループとは何か</h2><p>Python の <code>while</code> ループの一般的な構文は次のようになります。</p><pre><code class="language-python">while condition:
    execute this code in the loop's body
</code></pre><p>while ループは、条件が True の間コードを実行します。その条件が True でなくなるまで、必要なコード文のセットを実行し続けます。</p><p>while ループは、実行する前に必ずまず条件をチェックします。</p><p>条件が <code>True</code> となった場合、ループはループ内のコードを実行します。</p><p>たとえば、次のループは、<code>number</code> が <code>10</code> 未満である限り実行されます。</p><pre><code class="language-python">number = 0
while number &lt; 10:
    print(f"Number is {number}!")
    number = number + 1
</code></pre><p>出力:</p><pre><code>Number is 0!
Number is 1!
Number is 2!
Number is 3!
Number is 4!
Number is 5!
Number is 6!
Number is 7!
Number is 8!
Number is 9!
</code></pre><p>ここでは、最初に変数 <code>number</code> が <code>0</code> に設定されています。</p><p>コードが実行される前に、Python は条件 (<code>number &lt; 10</code>) をチェックします。True と判断されるため、print 文が実行され、<code>Number is 0!</code> とコンソールに出力されます。</p><p>その後、<code>number</code> の値は <code>1</code> 増加します。条件が再確認され、再び True であるため、<code>number</code> が <code>9</code> に等しくなるまで全体の手順が繰り返されます。</p><p><code>Number is 9!</code> と出力された後、<code>number</code> の値は増加しますが、<code>number</code> は <code>10</code> に等しくなるため、その時点で条件が満たされなくなり、ループは終了します。</p><p>下の例のように、条件を満たさない場合、<code>while</code> ループが全く実行されない可能性もあります。</p><pre><code class="language-python">number = 50
while number &lt; 10 :
    print(f"Number is {number}!")
</code></pre><p>条件は常に False であるため、ループ内のコードは実行されません。</p><h3 id="-">無限ループを作らない</h3><p>上の例でもわかるように、<code>while</code> ループには通常、ループ内に値が変化する変数が伴います。そして最終的にループがいつ終了するかが決まっています。</p><p>この 1 行を追加しないと、無限ループが作成されてしまいます。</p><p><code>number</code> の値は増加も更新もされません。常に最初に設定された <code>0</code> のままであるため、<code>number &lt; 10</code> という条件は永久に True となってしまいます。これは、ループが永遠に続いてしまうことを意味します。</p><pre><code class="language-python">
# don't run this

number = 0
while number &lt; 10:
    print(f"Number is {number}!")
</code></pre><p>出力：</p><pre><code>Number is 0!
Number is 0!
Number is 0!
Number is 0!
Number is 0!
Number is 0!
Number is 0!
...
</code></pre><p>このコードは無限に実行されてしまいます。</p><p>これは、下のコードと同じことです。</p><pre><code class="language-python">
#don't run this
while True:
    print("I am always true")
</code></pre><p>このような状況に陥ったらどうしますか？</p><p><code>Ctrl + C</code> を押すと、ループを終了することができます。</p><h2 id="do-while-">do while ループとは何か</h2><p>他のプログラミング言語における <code>do while</code> ループの一般的な構文は次のようになります。</p><pre><code>do {
  loop block statement to be executed;
  }
while(condition);
</code></pre><p>たとえば、C の do while ループは次のようになります。</p><pre><code class="language-c">#include &lt;stdio.h&gt;
 
int main(void)
 {
   int i = 10;
   do {
      printf("the value of i: %i\n", i);
      i++;
      }
  while( i &lt; 20 );
 }
</code></pre><p>do while ループのユニークな点は、ループブロック内のコードが少なくとも 1 回は実行されるということです。</p><p>ブロック内のコードは 1 回実行され、コードの実行<em>後にのみ</em>条件が確認されます。</p><p>したがって、最初にコードが 1 度実行されてから、条件がチェックされます。</p><p>チェックされた条件が true と判断されると、ループが続行されます。</p><p>コードを少なくとも一度は実行したい場合に、do while ループは便利です。</p><p>たとえば、ユーザーからの入力を受け取るプログラムを作成している場合に、正数のみを求めることがあります。コードは少なくとも 1 回は実行されます。ユーザーが送信した数値が負数の場合、ループは実行し続けます。プラスの場合は停止します。</p><p>Python には他の言語のように <code>do while</code> ループを明確に作成するための組み込み機能はありません。しかし、Python でも <code>do while</code> ループと同じ機能を果たすことは可能です。</p><h2 id="python-do-while-">Python で do while ループを作成する方法</h2><p>Python で <code>do while</code> ループを作成するには、他の言語の <code>do while</code> ループと同様な機能を果たすように、<code>while</code> ループに少し手を加える必要があります。</p><p>これまでのおさらいですが、<code>do while</code> ループは少なくとも 1 回実行されます。条件が満たされると、再度実行されます。</p><p>一方で、<code>while</code> ループは必ず一度は実行されるというわけではなく、実際には一度も実行されないこともあります。条件が満たされたときのみに実行されます。</p><p>では、コード行を少なくとも 1 回は実行したいとしましょう。</p><pre><code class="language-python">secret_word = "python"
counter = 0

while True:
    word = input("Enter the secret word: ").lower()
    counter = counter + 1
    if word == secret_word:
        break
    if word != secret_word and counter &gt; 7: 
        break
</code></pre><p>このコードは少なくとも一度は実行され、ユーザーに入力を求めます。</p><p><code>True</code> があることによって、少なくとも 1 回は実行されることが保証されます。この <code>True</code> は使い方を誤ると無限ループになってしまうため注意してください。</p><p>ユーザーが正しいシークレットワードを入力することで、ループは終了します。</p><p>ユーザーが間違ったシークレットワードを 7 回以上入力すると、ループは終了します。</p><p><code>break</code> 文を使用すると、<code>while</code> ループのフローを制御し、無限ループに陥ってしまうのを防ぐことができます。</p><p><code>break</code> 文は現ループを直ちに終了させます。</p><p>これが、Python で <code>do while</code> ループと同様の機能を作る方法です。</p><p>このループのコードは必ず少なくとも 1 回は実行されます。条件が満たされない場合はループを継続し、条件が満たされた時点で終了します。</p><h2 id="--1">まとめ</h2><p>これで、Python で <code>do while</code> ループを作成する方法がわかりました。</p><p>Python についてさらに詳しく知りたい場合は、freeCodeCamp の YouTube チャンネルの動画、<a href="https://www.youtube.com/watch?v=8ext9G7xspg&amp;t=40s">Python 12 のプロジェクト</a>をご覧ください。初心者向けの動画で、12 個のプロジェクトを作成しながら学べます。</p><p>また、freeCodeCamp には、Python の基礎を十分に理解し、包括的な理解を深められる、無料の <a href="https://www.freecodecamp.org/learn/scientific-computing-with-python/">Python 認定講座</a>もあります。</p><p>認定講座の最後には、学んだことを実践するために 5 つのプロジェクトを作成します。</p><p>読んでいただき、ありがとうございました。Happy learning!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Python における %.2f – その意味とは？ ]]>
                </title>
                <description>
                    <![CDATA[ Python では、データ型をフォーマットするためのさまざまな方法があります。 %f フォーマッタは、主に浮動小数点数 (小数を含む数値) を書式設定する際に使用されます。 %f フォーマッタを使うと、浮動小数点数を切り上げたときに返される小数点以下の位を指定することができるのです。 Python で %f フォーマッタを使用する方法 このセクションでは、%f フォーマッタの使い方と、異なるパラメータを使うことで、違う結果を返す使用例をいくつか紹介します。 最初の例は次のとおりです。 floatNumber = 1.9876 print("%f" % floatNumber) # 1.987600 上の例のように %f を使用すると、数値に 2 つのゼロが追加されました。しかし、特筆すべきことは何もありません。結果の値を変更するために他に何ができるかを見ていきましょう。 %f フォーマッタは引用符で囲んでネストする必要があり、フォーマットする浮動小数点数とはモジュロ演算子 (%) で、"%f" % floatNumber  のように区切らなければなりません。 他の例も ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/2f-in-python-what-does-it-mean/</link>
                <guid isPermaLink="false">65301b2d1a8daf03fb83ce39</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Saki Basken ]]>
                </dc:creator>
                <pubDate>Sun, 22 Oct 2023 11:28:40 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2023/10/susan-holt-simpson-GQ327RPuxhI-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/2f-in-python-what-does-it-mean/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">%.2f in Python – What does it Mean?</a>
      </p><p>Python では、データ型をフォーマットするためのさまざまな方法があります。</p><p><code>%f</code> フォーマッタは、主に浮動小数点数 (小数を含む数値) を書式設定する際に使用されます。</p><p><code>%f</code> フォーマッタを使うと、浮動小数点数を切り上げたときに返される小数点以下の位を指定することができるのです。</p><h2 id="python-f-">Python で <code>%f</code> フォーマッタを使用する方法</h2><p>このセクションでは、<code>%f</code> フォーマッタの使い方と、異なるパラメータを使うことで、違う結果を返す使用例をいくつか紹介します。</p><p>最初の例は次のとおりです。</p><pre><code class="language-python">floatNumber = 1.9876

print("%f" % floatNumber)
# 1.987600</code></pre><p>上の例のように <code>%f</code> を使用すると、数値に 2 つのゼロが追加されました。しかし、特筆すべきことは何もありません。結果の値を変更するために他に何ができるかを見ていきましょう。</p><p><code>%f</code> フォーマッタは引用符で囲んでネストする必要があり、フォーマットする浮動小数点数とはモジュロ演算子 (%) で、<code>"%f" % floatNumber</code> のように区切らなければなりません。</p><p>他の例もみてみましょう。</p><pre><code class="language-python">floatNumber = 1.9876

print("%.1f" % floatNumber)
# 2.0</code></pre><p>上記のコードでは、<code>%f</code> 演算子 % と f の間に .1 を追加しました。これは、数字を小数点第 1 位まで切り上げることを意味します。</p><p>% と f の間に渡した数字の前にあるピリオドまたはドット記号 (.) を省略すると、最初の例と似た結果が得られることに注意してください。</p><p>例で得られた結果は、1.9876 が小数点第 1 位まで切り上げられて 2.0 になったものです。</p><p><code>%.2f</code> を使うとどうなるかみてみましょう。</p><pre><code class="language-python">floatNumber = 1.9876

print("%.2f" % floatNumber)
# 1.99</code></pre><p>予想どおり、浮動小数点数 (1.9876) は小数点第 2 位に切り上げられ、1.99 になりました。したがって、<code>%.2f</code> は小数点以下第 2 位に切り上げることを意味します。</p><p>コードを使って、フォーマッタで数値を変更すると何が起こるかをいろいろ試してみてください。</p><h2 id="python-d-">Python で<strong> </strong><code><strong>%d</strong></code><strong> </strong>フォーマッタを使用する方法</h2><p>Python で浮動小数点数に使用できるもう 1 つの書式設定方法は、<code>%d</code> フォーマッタです。これは浮動小数点数の整数部分を返します。</p><p>以下はその例になります。</p><pre><code class="language-python">floatNumber = 1.9876

print("%d" % floatNumber)
# 1</code></pre><p>上の例では、<code>floatNumber = 1.9876</code> という浮動小数点数を作成しました。</p><p><code>floatNumber</code> 変数が <code>%d</code> を使用してフォーマットされたので、1 のみが返されました。</p><p><code>%d</code> フォーマッタは小数部分を無視し、整数のみを返します。</p><h2 id="-">まとめ</h2><p>この記事では、Python の <code>%f</code> フォーマッタについて説明しました。これを使用することによって、浮動小数点数の位を指定することができます。</p><p>指定されたパラメータに応じて、<code>%f</code> フォーマッタは浮動小数点値を指定された最も近い小数点以下の桁に切り上げます。</p><p>浮動小数点数の整数部分のみを返す <code>%d</code> フォーマッタについても説明しました。</p><p>Happy coding!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 40 以上の言語でコンパイルおよび実行できる React を使用したコードエディターを構築する方法 ]]>
                </title>
                <description>
                    <![CDATA[ オンラインのコード実行プラットフォームを利用すれば、あなたのお気に入りのプログラミング言語でコードを書き、そのコードを同じプラットフォーム上で実行することができます。 自分で作成したプログラム (たとえば、JavaScript で作成されたバイナリ検索プログラム) の出力を確認できると理想的です。 今日は、40 以上の異なるプログラミング言語でコードをコンパイルおよび実行できる CodeRush と呼ばれるオンラインコード実行プラットフォームを構築していきます。 構築するもの Source Code [https://github.com/manuarora700/react-code-editor] | Live Demo [https://coderush.vercel.app/] 以下の機能を備えた多彩なコードエディターを構築します。  * VS Code にも使われているコードエディター (Monaco Editor    [https://www.npmjs.com/package/monaco-editor])。  * 40 を超えるプログラミング言語をサポートし ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/how-to-build-react-based-code-editor/</link>
                <guid isPermaLink="false">64dd06a151868d0400507820</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Saki Basken ]]>
                </dc:creator>
                <pubDate>Mon, 28 Aug 2023 10:18:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2023/08/Build-a-React-Code-Editor-That-Compiles-and-Executes-in-10--Languages--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/how-to-build-react-based-code-editor/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Build a Code Editor with React that Compiles and Executes in 40+ Languages</a>
      </p><p>オンラインのコード実行プラットフォームを利用すれば、あなたのお気に入りのプログラミング言語でコードを書き、そのコードを同じプラットフォーム上で実行することができます。</p><p>自分で作成したプログラム (たとえば、JavaScript で作成されたバイナリ検索プログラム) の出力を確認できると理想的です。</p><p>今日は、40 以上の異なるプログラミング言語でコードをコンパイルおよび実行できる CodeRush と呼ばれるオンラインコード実行プラットフォームを構築していきます。</p><h2 id="-">構築するもの</h2><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/08/Screenshot-2022-05-18-at-9.05.14-PM.png" class="kg-image" alt="Screenshot-2022-05-18-at-9.05.14-PM" width="2000" height="1142" loading="lazy"></figure><p><a href="https://github.com/manuarora700/react-code-editor">Source Code</a> | <a href="https://coderush.vercel.app/">Live Demo</a></p><p>以下の機能を備えた多彩なコードエディターを構築します。</p><ul><li>VS Code にも使われているコードエディター (<a href="https://www.npmjs.com/package/monaco-editor">Monaco Editor</a>)。</li><li>40 を超えるプログラミング言語をサポートし、標準入出力を使用して Web アプリ上のコードをコンパイルできる。</li><li>利用可能なテーマのリストからエディターのテーマを変更できる。</li><li>実行されたコードに関する情報 (コードにかかった時間、使用されたメモリ、ステータスなど) を取得できる。</li></ul><h2 id="--1">技術スタック</h2><p>このプロジェクトでは、以下の技術スタックを使用します。</p><ul><li><a href="https://reactjs.org/">React.js</a> – フロントエンド用</li><li><a href="https://tailwindcss.com/">TailwindCSS</a> – スタイル用</li><li><a href="https://judge0.com/">Judge0</a> – コードのコンパイルおよび実行用</li><li><a href="https://rapidapi.com/">RapidAPI</a> – Judge0 コードの迅速なデプロイ</li><li><a href="https://www.npmjs.com/package/monaco-editor">Monaco Editor</a> – プロジェクトを支えるコードエディター</li></ul><h2 id="--2">プロジェクトの構造</h2><p>プロジェクトの構造は非常にシンプルで理解しやすくなっています。</p><ul><li><strong>Components:</strong> すべてのコンポーネント又は再利用可能なコードスニペットがここにあります。(例: CodeEditorWindow や Landing)</li><li><strong>hooks</strong>: ここにすべてのカスタムフックがあります。(キーボードイベントを使用してコードをコンパイルするためのキープレスフックを使用する予定です。)</li><li><strong><strong>lib</strong></strong>: すべてのライブラリ関数はここにあります。(ここでテーマを定義する関数を作成します。)</li><li><strong><strong>constants</strong></strong>: ドロップダウンの <code>languageOptions</code> や <code>customStyles</code> などのすべての定数がここに入力されます。</li><li><strong><strong>utils</strong></strong>: コードの保守に役立つ一般的なユーティリティー関数がここにあります。</li></ul><h4 id="--3">アプリケーションの流れ</h4><p>コードを深く掘り下げる前に、まずアプリケーションの流れと、ゼロからコードを書いていく方法を把握しましょう。</p><ul><li>ユーザーは Web アプリケーションにアクセスし、好みのプログラミング言語 (デフォルトは JavaScript) を選択できます。</li><li>ユーザーがコードの作成を完了すると、コードをコンパイルし、出力ウィンドウで出力、結果を確認できます。</li><li>コード出力ウィンドウには、コードスニペットの成功または失敗が表示されます。すべての情報がコード出力ウィンドウで確認できます。</li><li>ユーザーはコードスニペットにカスタム入力を追加でき、Judge0 (オンラインコンパイラー) はユーザーが指定したカスタム入力を考慮します。</li><li>ユーザーは、実行されたコードに関する関連情報を確認できます。(例: コードのコンパイルと実行に 5 ms かかり、2024 kb のメモリが使用され、ランタイムステータスは成功)</li></ul><p>フォルダー構造とアプリケーションの流れについて少し理解できたところで、コードを詳しく見て、すべてがどのように機能するかをみてみましょう。</p><h2 id="--4">コードエディターコンポーネントを構築する方法</h2><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/08/Screenshot-2022-05-18-at-9.26.48-PM.png" class="kg-image" alt="Screenshot-2022-05-18-at-9.26.48-PM" width="1992" height="1440" loading="lazy"></figure><p>コードエディターコンポーネントは主に、使用およびカスタマイズできる NPM パッケージ、Monaco Editor で構成されます。</p><pre><code class="language-javascript">// CodeEditorWindow.js

import React, { useState } from "react";

import Editor from "@monaco-editor/react";

const CodeEditorWindow = ({ onChange, language, code, theme }) =&gt; {
  const [value, setValue] = useState(code || "");

  const handleEditorChange = (value) =&gt; {
    setValue(value);
    onChange("code", value);
  };

  return (
    &lt;div className="overlay rounded-md overflow-hidden w-full h-full shadow-4xl"&gt;
      &lt;Editor
        height="85vh"
        width={`100%`}
        language={language || "javascript"}
        value={value}
        theme={theme}
        defaultValue="// some comment"
        onChange={handleEditorChange}
      /&gt;
    &lt;/div&gt;
  );
};
export default CodeEditorWindow;
</code></pre><p><code>Editor</code> コンポーネントは <code>@monaco-editor/react</code> パッケージから取得されたもので、指定された高さ <code>85vh</code> でコードエディターを起動できます。</p><p><code>Editor</code> コンポーネントはいくつかの props を受け取ります:</p><ul><li><code>language</code>: 構文ハイライトやインテリセンスのために必要な、言語を指定する必須項目。</li><li><code>theme</code>: コードスニペットの色と背景 (チュートリアルの後半で構成します)。</li><li><code>value</code>: コードエディターに入力される実際のコード値。</li><li><code>onChange</code>: これは、コードエディターの値が変更されたときにトリガーされます。後で Judge0 API を呼び出してコンパイルできるように、変更された値を状態に保存する必要があります。</li></ul><p>エディターは、その親コンポーネントである <code>Landing.js</code> から、<code>onChange</code>、<code>language</code>、<code>code</code>、<code>theme</code> の props を受け取ります。コードエディター内の値が変更されるたびに、親コンポーネントの <code>Landing</code> 内にある <code>onChange</code> ハンドラーが呼び出されます。</p><h2 id="--5">ランディングコンポーネントを構築する</h2><p>ランディングコンポーネントは主に 3 つの部分で構成されています。</p><ul><li><code>language</code> と <code>theme</code> のドロップダウンコンポーネントを備えた <code>Actions Bar</code></li><li><code>Code Editor Window</code> コンポーネント</li><li><code>Output and Custom Input</code> コンポーネント</li></ul><pre><code class="language-javascript">// Landing.js

import React, { useEffect, useState } from "react";
import CodeEditorWindow from "./CodeEditorWindow";
import axios from "axios";
import { classnames } from "../utils/general";
import { languageOptions } from "../constants/languageOptions";

import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";

import { defineTheme } from "../lib/defineTheme";
import useKeyPress from "../hooks/useKeyPress";
import Footer from "./Footer";
import OutputWindow from "./OutputWindow";
import CustomInput from "./CustomInput";
import OutputDetails from "./OutputDetails";
import ThemeDropdown from "./ThemeDropdown";
import LanguagesDropdown from "./LanguagesDropdown";

const javascriptDefault = `// some comment`;

const Landing = () =&gt; {
  const [code, setCode] = useState(javascriptDefault);
  const [customInput, setCustomInput] = useState("");
  const [outputDetails, setOutputDetails] = useState(null);
  const [processing, setProcessing] = useState(null);
  const [theme, setTheme] = useState("cobalt");
  const [language, setLanguage] = useState(languageOptions[0]);

  const enterPress = useKeyPress("Enter");
  const ctrlPress = useKeyPress("Control");

  const onSelectChange = (sl) =&gt; {
    console.log("selected Option...", sl);
    setLanguage(sl);
  };

  useEffect(() =&gt; {
    if (enterPress &amp;&amp; ctrlPress) {
      console.log("enterPress", enterPress);
      console.log("ctrlPress", ctrlPress);
      handleCompile();
    }
  }, [ctrlPress, enterPress]);
  const onChange = (action, data) =&gt; {
    switch (action) {
      case "code": {
        setCode(data);
        break;
      }
      default: {
        console.warn("case not handled!", action, data);
      }
    }
  };
  const handleCompile = () =&gt; {
    // We will come to the implementation later in the code
  };

  const checkStatus = async (token) =&gt; {
    // We will come to the implementation later in the code
  };

  function handleThemeChange(th) {
    // We will come to the implementation later in the code
  }
  useEffect(() =&gt; {
    defineTheme("oceanic-next").then((_) =&gt;
      setTheme({ value: "oceanic-next", label: "Oceanic Next" })
    );
  }, []);

  const showSuccessToast = (msg) =&gt; {
    toast.success(msg || `Compiled Successfully!`, {
      position: "top-right",
      autoClose: 1000,
      hideProgressBar: false,
      closeOnClick: true,
      pauseOnHover: true,
      draggable: true,
      progress: undefined,
    });
  };
  const showErrorToast = (msg) =&gt; {
    toast.error(msg || `Something went wrong! Please try again.`, {
      position: "top-right",
      autoClose: 1000,
      hideProgressBar: false,
      closeOnClick: true,
      pauseOnHover: true,
      draggable: true,
      progress: undefined,
    });
  };

  return (
    &lt;&gt;
      &lt;ToastContainer
        position="top-right"
        autoClose={2000}
        hideProgressBar={false}
        newestOnTop={false}
        closeOnClick
        rtl={false}
        pauseOnFocusLoss
        draggable
        pauseOnHover
      /&gt;
      &lt;div className="h-4 w-full bg-gradient-to-r from-pink-500 via-red-500 to-yellow-500"&gt;&lt;/div&gt;
      &lt;div className="flex flex-row"&gt;
        &lt;div className="px-4 py-2"&gt;
          &lt;LanguagesDropdown onSelectChange={onSelectChange} /&gt;
        &lt;/div&gt;
        &lt;div className="px-4 py-2"&gt;
          &lt;ThemeDropdown handleThemeChange={handleThemeChange} theme={theme} /&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div className="flex flex-row space-x-4 items-start px-4 py-4"&gt;
        &lt;div className="flex flex-col w-full h-full justify-start items-end"&gt;
          &lt;CodeEditorWindow
            code={code}
            onChange={onChange}
            language={language?.value}
            theme={theme.value}
          /&gt;
        &lt;/div&gt;

        &lt;div className="right-container flex flex-shrink-0 w-[30%] flex-col"&gt;
          &lt;OutputWindow outputDetails={outputDetails} /&gt;
          &lt;div className="flex flex-col items-end"&gt;
            &lt;CustomInput
              customInput={customInput}
              setCustomInput={setCustomInput}
            /&gt;
            &lt;button
              onClick={handleCompile}
              disabled={!code}
              className={classnames(
                "mt-4 border-2 border-black z-10 rounded-md shadow-[5px_5px_0px_0px_rgba(0,0,0)] px-4 py-2 hover:shadow transition duration-200 bg-white flex-shrink-0",
                !code ? "opacity-50" : ""
              )}
            &gt;
              {processing ? "Processing..." : "Compile and Execute"}
            &lt;/button&gt;
          &lt;/div&gt;
          {outputDetails &amp;&amp; &lt;OutputDetails outputDetails={outputDetails} /&gt;}
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;Footer /&gt;
    &lt;/&gt;
  );
};
export default Landing;
</code></pre><p>ランディングページの基本構造をさらに詳しくみてみましょう。</p><h3 id="codeeditorwindow-"><strong>CodeEditorWindow</strong> コンポーネント</h3><p>上記に説明したように、CodeEditorWindow コンポーネントは (変更され続ける) コードと、コード内の変更を追跡する <code>onChange</code> メソッドを考慮します。</p><pre><code class="language-javascript">// onChange method implementation

 const onChange = (action, data) =&gt; {
    switch (action) {
      case "code": {
        setCode(data);
        break;
      }
      default: {
        console.warn("case not handled!", action, data);
      }
    }
  };
</code></pre><p><code>code</code> の状態を設定し、変更を追跡するだけでいいのです。</p><p><code>CodeEditorWindow</code> コンポーネントは、構文の強調表示とインテリセンスが必要な現在選択されている言語である <code>language</code> の props も考慮します。</p><p>Monaco Editor によって受け入れられた language props を追跡し、コンパイルも処理する <code>languageOptions</code> 配列を作成しました。(<code>judge0</code> API によって受け入れられる <code>languageId</code> を追跡します)</p><pre><code class="language-javascript">// constants/languageOptions.js

export const languageOptions = [
  {
    id: 63,
    name: "JavaScript (Node.js 12.14.0)",
    label: "JavaScript (Node.js 12.14.0)",
    value: "javascript",
  },
  {
    id: 45,
    name: "Assembly (NASM 2.14.02)",
    label: "Assembly (NASM 2.14.02)",
    value: "assembly",
  },
    ...
    ...
    ...
    ...
    ...
    ...
    
  {
    id: 84,
    name: "Visual Basic.Net (vbnc 0.0.0.5943)",
    label: "Visual Basic.Net (vbnc 0.0.0.5943)",
    value: "vbnet",
  },
];
</code></pre><p>すべての <code>languageOptions</code> オブジェクトには、<code>id</code>、<code>name</code>、<code>label</code>、および <code>value</code> キーが含まれています。<code>languageOptions</code> 配列全体を取得してドロップダウン内に配置し、オプションとして指定できます。</p><p>ドロップダウンの状態が変化するたびに、<code>onSelectChange</code> メソッドは選択された <code>id</code> を追跡し、状態を適切に変更します。</p><h3 id="languagedropdown-"><strong>LanguageDropdown コンポーネント</strong></h3><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/08/Screenshot-2022-05-19-at-10.42.43-PM.png" class="kg-image" alt="Screenshot-2022-05-19-at-10.42.43-PM" width="992" height="176" loading="lazy"></figure><pre><code class="language-javascript">// LanguageDropdown.js

import React from "react";
import Select from "react-select";
import { customStyles } from "../constants/customStyles";
import { languageOptions } from "../constants/languageOptions";

const LanguagesDropdown = ({ onSelectChange }) =&gt; {
  return (
    &lt;Select
      placeholder={`Filter By Category`}
      options={languageOptions}
      styles={customStyles}
      defaultValue={languageOptions[0]}
      onChange={(selectedOption) =&gt; onSelectChange(selectedOption)}
    /&gt;
  );
};

export default LanguagesDropdown;

</code></pre><p>ドロップダウンの場合は、ドロップダウンとその変更ハンドラーを処理する <a href="https://react-select.com/">react-select</a> パッケージを使用します。</p><p>React select は、<code>defaultValue</code> と <code>options</code> を主要引数として受け取ります。<code>options</code> は、関連するすべてのドロップダウン値を自動的に表示する配列 (ここでは <code>languageOptions</code> を渡します) です。</p><p><code>defaultValue</code> という props は、コンポーネントに提供されるデフォルト値です。ここではデフォルト言語として JavaScript を選択します。(これは言語の配列内で最初の言語です。)</p><p>ユーザーが言語を変更するたびに、<code>onSelectChange</code> コールバックを使用して言語を変更します。</p><pre><code class="language-javascript">const onSelectChange = (sl) =&gt; {
    setLanguage(sl);
};
</code></pre><h3 id="themedropdown-"><strong>ThemeDropdown コンポーネント</strong></h3><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/08/Screenshot-2022-05-19-at-10.42.43-PM-1-1.png" class="kg-image" alt="Screenshot-2022-05-19-at-10.42.43-PM-1-1" width="992" height="176" loading="lazy"></figure><p><code>ThemeDropdown</code> コンポーネントは、実は (UI および React-select パッケージを含む) <code>LanguageDropdown</code> コンポーネントに非常に似ています。</p><pre><code class="language-javascript">// ThemeDropdown.js

import React from "react";
import Select from "react-select";
import monacoThemes from "monaco-themes/themes/themelist";
import { customStyles } from "../constants/customStyles";

const ThemeDropdown = ({ handleThemeChange, theme }) =&gt; {
  return (
    &lt;Select
      placeholder={`Select Theme`}
      // options={languageOptions}
      options={Object.entries(monacoThemes).map(([themeId, themeName]) =&gt; ({
        label: themeName,
        value: themeId,
        key: themeId,
      }))}
      value={theme}
      styles={customStyles}
      onChange={handleThemeChange}
    /&gt;
  );
};

export default ThemeDropdown;
</code></pre><p>ここでは、Monaco Editor 用にネット上で入手可能で、多種のきれいなデザインテーマを使用することができる、<code>MonacoThemes</code> というパッケージを利用します。</p><p>自由に利用できるテーマのリストがこちらです。</p><pre><code class="language-javascript">// lib/defineTheme.js

import { loader } from "@monaco-editor/react";

const monacoThemes = {
  active4d: "Active4D",
  "all-hallows-eve": "All Hallows Eve",
  amy: "Amy",
  "birds-of-paradise": "Birds of Paradise",
  blackboard: "Blackboard",
  "brilliance-black": "Brilliance Black",
  "brilliance-dull": "Brilliance Dull",
  "chrome-devtools": "Chrome DevTools",
  "clouds-midnight": "Clouds Midnight",
  clouds: "Clouds",
  cobalt: "Cobalt",
  dawn: "Dawn",
  dreamweaver: "Dreamweaver",
  eiffel: "Eiffel",
  "espresso-libre": "Espresso Libre",
  github: "GitHub",
  idle: "IDLE",
  katzenmilch: "Katzenmilch",
  "kuroir-theme": "Kuroir Theme",
  lazy: "LAZY",
  "magicwb--amiga-": "MagicWB (Amiga)",
  "merbivore-soft": "Merbivore Soft",
  merbivore: "Merbivore",
  "monokai-bright": "Monokai Bright",
  monokai: "Monokai",
  "night-owl": "Night Owl",
  "oceanic-next": "Oceanic Next",
  "pastels-on-dark": "Pastels on Dark",
  "slush-and-poppies": "Slush and Poppies",
  "solarized-dark": "Solarized-dark",
  "solarized-light": "Solarized-light",
  spacecadet: "SpaceCadet",
  sunburst: "Sunburst",
  "textmate--mac-classic-": "Textmate (Mac Classic)",
  "tomorrow-night-blue": "Tomorrow-Night-Blue",
  "tomorrow-night-bright": "Tomorrow-Night-Bright",
  "tomorrow-night-eighties": "Tomorrow-Night-Eighties",
  "tomorrow-night": "Tomorrow-Night",
  tomorrow: "Tomorrow",
  twilight: "Twilight",
  "upstream-sunburst": "Upstream Sunburst",
  "vibrant-ink": "Vibrant Ink",
  "xcode-default": "Xcode_default",
  zenburnesque: "Zenburnesque",
  iplastic: "iPlastic",
  idlefingers: "idleFingers",
  krtheme: "krTheme",
  monoindustrial: "monoindustrial",
};

const defineTheme = (theme) =&gt; {
  return new Promise((res) =&gt; {
    Promise.all([
      loader.init(),
      import(`monaco-themes/themes/${monacoThemes[theme]}.json`),
    ]).then(([monaco, themeData]) =&gt; {
      monaco.editor.defineTheme(theme, themeData);
      res();
    });
  });
};

export { defineTheme };
</code></pre><p><code>monaco-themes</code> というパッケージは、コードエディターのデザインの方向性を定義するために使用できる多数のテーマを提供します。</p><p><code>defineTheme</code> 関数は、ユーザーが選択するであろうさまざまなテーマを扱います。<code>defineTheme</code> 関数は、<code>monaco.editor.defineTheme(theme,themeData)</code> アクションを使用して実際に Monaco Editor のテーマを設定する Promise を返します。このコード行は、Monaco Editor コードウィンドウ内のテーマを実際に変更する役割を担っています。</p><p><code>defineTheme</code> 関数は、先ほど <code>ThemeDropdown.js</code> コンポーネントで見た <code>onChange</code> コールバックを利用して呼び出されます。</p><pre><code class="language-javascript">// Landing.js - handleThemeChange() function

function handleThemeChange(th) {
    const theme = th;
    console.log("theme...", theme);

    if (["light", "vs-dark"].includes(theme.value)) {
      setTheme(theme);
    } else {
      defineTheme(theme.value).then((_) =&gt; setTheme(theme));
    }
  }
  
</code></pre><p><code>handleThemeChange()</code> 関数は、テーマが <code>light</code> か <code>dark</code> かをチェックします。これらのテーマはデフォルトで <code>MonacoEditor</code> コンポーネントで利用できるため、<code>defineTheme()</code> メソッドを呼び出す必要はありません。</p><p>そうでない場合は、<code>defineTheme()</code> コンポーネントを呼び出して、選択したテーマの状態を設定します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/08/Screenshot-2022-05-19-at-10.46.34-PM.png" class="kg-image" alt="Screenshot-2022-05-19-at-10.46.34-PM" width="1992" height="1596" loading="lazy"></figure><h2 id="judge0-">Judge0 を使用してコードをコンパイルする方法</h2><p>アプリケーションの根幹部である、さまざまな言語でコードをコンパイルする方法を見ていきましょう。</p><p>コードをコンパイルするには、Judge0 を使用します。Judge0 は、対話可能でかつシンプルな、オープンソースのコード実行システムです。</p><p>いくつかの引数 (ソースコード、言語 ID) を使用して単純な API 呼び出しを実行し、応答として出力を取得することができます。</p><p>Judge0 をセットアップして、次のステップに進みましょう。</p><ul><li><a href="https://judge0.com/">Judge0</a> に移動し、ベーシックプランを選択します</li><li>Judge0 は実際は <a href="https://rapidapi.com/">RapidAPI</a> でホスティングされています。ベーシックプランに登録してください。</li><li>登録すると、コード実行システムへの API 呼び出しを行うために必要な <code>RAPIDAPI_HOST</code> と <code>RAPIDAPI_KEY</code> をコピーできます。</li></ul><p>ダッシュボードは以下のようなものになります。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/08/Untitled-design.png" class="kg-image" alt="Untitled-design" width="2000" height="973" loading="lazy"></figure><p>API 呼び出しには <code>X-RapidAPI-Host</code> 引数と <code>X-RapidAPI-Key</code> 引数が必要です。後ほど使用できるように、次のように <code>.env</code> ファイルに保存します。</p><pre><code class="language-env">REACT_APP_RAPID_API_HOST = YOUR_HOST_URL
REACT_APP_RAPID_API_KEY = YOUR_SECRET_KEY
REACT_APP_RAPID_API_URL = YOUR_SUBMISSIONS_URL
</code></pre><p>React では、環境変数に <code>REACT_APP</code> というプレフィックスを最初に含めることが重要です。</p><p><code>SUBMISSIONS_URL</code> は、使用する URL です。これは基本的に <code>host</code> とそれに続く <code>/submission</code> ルートで構成されます。</p><p>例えばこの場合では、<code>https://judge0-ce.p.rapidapi.com/submissions</code> が <code>submissions</code> URL になります。</p><p>変数を正しく設定したら、<code>compilation</code> (コンパイル) ロジックを処理できるようになります。</p><h4 id="--6"><strong>コンパイルフローとロジック</strong></h4><p>コンパイルの流れは以下の通りです。</p><ul><li><code>Compile and Execute</code> ボタンをクリックすると、<code>handleCompile()</code> 関数が呼び出されます。</li><li><code>handleCompile()</code> 関数は、<code>languageId</code>、<code>source_code</code>、<code>stdin</code> (この場合は customInput) を本体引数として、<code>submissions</code> URL 上で <code>Judge0 RapidAPI</code> のバックエンドにリクエストします。</li><li>the <code>options</code> は <code>host</code> と <code>secret</code> をヘッダーとして受け取ります。</li><li><code>base64_encoded</code> と <code>fields</code> は、渡すことができるオプションの引数です。</li><li><code>submission</code> POST リクエストはリクエストをサーバーに登録し、プロセスを作成します。<code>post</code> リクエストの応答は、後で実行のステータスを確認するために使用する <code>token</code> です。(処理中、承認済み、時間制限超過、実行時間の例外など、さまざまなステータスがあります。)</li><li>結果が返されると、結果が成功か失敗かを条件付きで確認し、結果を出力画面に表示します。</li></ul><p>実際にコードを見てみて、<code>handleCompile()</code> メソッドを理解しましょう。</p><pre><code class="language-javascript">const handleCompile = () =&gt; {
    setProcessing(true);
    const formData = {
      language_id: language.id,
      // encode source code in base64
      source_code: btoa(code),
      stdin: btoa(customInput),
    };
    const options = {
      method: "POST",
      url: process.env.REACT_APP_RAPID_API_URL,
      params: { base64_encoded: "true", fields: "*" },
      headers: {
        "content-type": "application/json",
        "Content-Type": "application/json",
        "X-RapidAPI-Host": process.env.REACT_APP_RAPID_API_HOST,
        "X-RapidAPI-Key": process.env.REACT_APP_RAPID_API_KEY,
      },
      data: formData,
    };

    axios
      .request(options)
      .then(function (response) {
        console.log("res.data", response.data);
        const token = response.data.token;
        checkStatus(token);
      })
      .catch((err) =&gt; {
        let error = err.response ? err.response.data : err;
        setProcessing(false);
        console.log(error);
      });
  };
</code></pre><p>上記の通り、<code>handleCompile()</code> メソッドは <code>languageId</code>、<code>source_code</code>、<code>stdin</code> を受け取ります。特に <code>source_code</code> と <code>stdin</code> の前に <code>btoa</code> が使用されていることに注目してください。これは、API に渡すパラメータで <code>base64_encoded: true</code> を使用しているのにあわせて、文字列を Base64 エンコードするためのものです。</p><p>応答が成功でトークンを入手したら、<code>checkStatus()</code> メソッドを呼び出して <code>/submissions/${token}</code> ルートをポーリングします。</p><pre><code class="language-javascript">const checkStatus = async (token) =&gt; {
    const options = {
      method: "GET",
      url: process.env.REACT_APP_RAPID_API_URL + "/" + token,
      params: { base64_encoded: "true", fields: "*" },
      headers: {
        "X-RapidAPI-Host": process.env.REACT_APP_RAPID_API_HOST,
        "X-RapidAPI-Key": process.env.REACT_APP_RAPID_API_KEY,
      },
    };
    try {
      let response = await axios.request(options);
      let statusId = response.data.status?.id;

      // Processed - we have a result
      if (statusId === 1 || statusId === 2) {
        // still processing
        setTimeout(() =&gt; {
          checkStatus(token)
        }, 2000)
        return
      } else {
        setProcessing(false)
        setOutputDetails(response.data)
        showSuccessToast(`Compiled Successfully!`)
        console.log('response.data', response.data)
        return
      }
    } catch (err) {
      console.log("err", err);
      setProcessing(false);
      showErrorToast();
    }
  };
</code></pre><p>以前に送信したコードの結果を取得するには、応答として受け取った <code>token</code> を使用して <code>submissions</code> API をポーリングする必要があります。</p><p>上記のように、エンドポイントに対して GET リクエストを行います。レスポンスがあると、<code>statusId === 1 || statusId === 2</code> をチェックしていますが、これはどういう意味なのでしょうか。</p><p>私たちが API に提出したコードに関連する合計 <code>14</code> のステータスがあります。それらは以下の通りです:</p><pre><code class="language-javascript">export const statuses = [
  {
    id: 1,
    description: "In Queue",
  },
  {
    id: 2,
    description: "Processing",
  },
  {
    id: 3,
    description: "Accepted",
  },
  {
    id: 4,
    description: "Wrong Answer",
  },
  {
    id: 5,
    description: "Time Limit Exceeded",
  },
  {
    id: 6,
    description: "Compilation Error",
  },
  {
    id: 7,
    description: "Runtime Error (SIGSEGV)",
  },
  {
    id: 8,
    description: "Runtime Error (SIGXFSZ)",
  },
  {
    id: 9,
    description: "Runtime Error (SIGFPE)",
  },
  {
    id: 10,
    description: "Runtime Error (SIGABRT)",
  },
  {
    id: 11,
    description: "Runtime Error (NZEC)",
  },
  {
    id: 12,
    description: "Runtime Error (Other)",
  },
  {
    id: 13,
    description: "Internal Error",
  },
  {
    id: 14,
    description: "Exec Format Error",
  },
];
</code></pre><p>したがって、<code>statusId ===1</code> または <code>statusId ===2</code> の場合、コードがまだ処理中であり、API を再度呼び出して結果が得られるかどうかを確認する必要があることを意味します。</p><p>このため、<code>if</code> 文の条件に <code>checkStatus()</code> 関数を再度呼び出す <code>setTimeout()</code> があり、内部で API を再度呼び出してステータスを確認します。</p><p>ステータスが <code>2</code> または <code>3</code> 以外の場合は、コードの実行が完了し、結果が得られたことを意味します。<code>successfully compiled</code> (コンパイル成功) か、<code>Time Limit Exceeded</code> (タイムリミット超過) か、あるいは <code>Runtime Exception</code> (ランタイム例外の発生) のいずれかになります。<code>statusId</code> は各シナリオを表し、これらを再現することができます。</p><p>例えば、<code>while(true)</code> では <code>time limit exceeded</code> のエラーが発生します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/08/Screenshot-2022-05-20-at-1.33.08-AM.png" class="kg-image" alt="Screenshot-2022-05-20-at-1.33.08-AM" width="2000" height="894" loading="lazy"></figure><p>または、構文に誤りがあると、コンパイルエラーが発生します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/08/Screenshot-2022-05-20-at-1.34.42-AM.png" class="kg-image" alt="Screenshot-2022-05-20-at-1.34.42-AM" width="2000" height="903" loading="lazy"></figure><p>どの場合においても、何らかの結果を得ることになります。そして、この結果を <code>outputDetails</code> の状態に保存します。これは、画面の右側 (出力ウィンドウ) に表示するものがあることを保証するためです。</p><h3 id="--7">出力ウィンドウコンポーネント</h3><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/08/Screenshot-2022-05-20-at-1.37.39-AM.png" class="kg-image" alt="Screenshot-2022-05-20-at-1.37.39-AM" width="896" height="936" loading="lazy"></figure><pre><code class="language-javascript">import React from "react";

const OutputWindow = ({ outputDetails }) =&gt; {
  const getOutput = () =&gt; {
    let statusId = outputDetails?.status?.id;

    if (statusId === 6) {
      // compilation error
      return (
        &lt;pre className="px-2 py-1 font-normal text-xs text-red-500"&gt;
          {atob(outputDetails?.compile_output)}
        &lt;/pre&gt;
      );
    } else if (statusId === 3) {
      return (
        &lt;pre className="px-2 py-1 font-normal text-xs text-green-500"&gt;
          {atob(outputDetails.stdout) !== null
            ? `${atob(outputDetails.stdout)}`
            : null}
        &lt;/pre&gt;
      );
    } else if (statusId === 5) {
      return (
        &lt;pre className="px-2 py-1 font-normal text-xs text-red-500"&gt;
          {`Time Limit Exceeded`}
        &lt;/pre&gt;
      );
    } else {
      return (
        &lt;pre className="px-2 py-1 font-normal text-xs text-red-500"&gt;
          {atob(outputDetails?.stderr)}
        &lt;/pre&gt;
      );
    }
  };
  return (
    &lt;&gt;
      &lt;h1 className="font-bold text-xl bg-clip-text text-transparent bg-gradient-to-r from-slate-900 to-slate-700 mb-2"&gt;
        Output
      &lt;/h1&gt;
      &lt;div className="w-full h-56 bg-[#1e293b] rounded-md text-white font-normal text-sm overflow-y-auto"&gt;
        {outputDetails ? &lt;&gt;{getOutput()}&lt;/&gt; : null}
      &lt;/div&gt;
    &lt;/&gt;
  );
};

export default OutputWindow;
</code></pre><p>これは、成功または失敗のシナリオのみを表示する、シンプルなコンポーネントです。</p><p><code>getOutput()</code> メソッドは、テキストの色がどのように見えるか、および何を表示するかを決定します。</p><ul><li><code>statusId</code> が <code>6</code> の場合: コンパイルエラーが発生しています。この場合、API は <code>compile_output</code> を返し、それを使用してエラーを表示できます。</li><li><code>statusId</code> が <code>3</code> の場合: これは <code>Accepted</code> という成功のシナリオです。この場合、API は <code>stdout</code> (標準出力) を返します。これは、API に提供したコードから返されたデータを表示するために使用されます。</li><li><code>statusId</code> が <code>5</code> の場合: 時間制限超過エラーが発生しています。コード内に無限ループ条件があるか、またはコード実行の標準の <code>5</code> 秒時間を超えていることを表示するのみです。</li><li>その他すべてのステータスについては、標準的な <code>stderr</code> オブジェクトを取得し、エラーを表示するために使用します。</li><li><code>atob()</code> メソッドが使用されている点に注意してください。これは、出力を Base64 文字列として受け取るためです。デコードするために <code>atob()</code> メソッドを使用します。</li></ul><p>以下は、JavaScript での <code>Binary Search</code> プログラムの成功シナリオです。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/08/Screenshot-2022-05-20-at-1.42.55-AM.png" class="kg-image" alt="Screenshot-2022-05-20-at-1.42.55-AM" width="892" height="974" loading="lazy"></figure><h3 id="--8">出力詳細コンポーネント</h3><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/08/Screenshot-2022-05-20-at-1.44.01-AM.png" class="kg-image" alt="Screenshot-2022-05-20-at-1.44.01-AM" width="900" height="428" loading="lazy"></figure><p><code>OutputDetails</code> コンポーネントは、最初にコンパイルしたコードスニペットに関連する詳細を表示するためのシンプルなマッパーです。データはすでに <code>outputDetails</code> 状態変数に設定されています。</p><pre><code class="language-javascript">import React from "react";

const OutputDetails = ({ outputDetails }) =&gt; {
  return (
    &lt;div className="metrics-container mt-4 flex flex-col space-y-3"&gt;
      &lt;p className="text-sm"&gt;
        Status:{" "}
        &lt;span className="font-semibold px-2 py-1 rounded-md bg-gray-100"&gt;
          {outputDetails?.status?.description}
        &lt;/span&gt;
      &lt;/p&gt;
      &lt;p className="text-sm"&gt;
        Memory:{" "}
        &lt;span className="font-semibold px-2 py-1 rounded-md bg-gray-100"&gt;
          {outputDetails?.memory}
        &lt;/span&gt;
      &lt;/p&gt;
      &lt;p className="text-sm"&gt;
        Time:{" "}
        &lt;span className="font-semibold px-2 py-1 rounded-md bg-gray-100"&gt;
          {outputDetails?.time}
        &lt;/span&gt;
      &lt;/p&gt;
    &lt;/div&gt;
  );
};

export default OutputDetails;
</code></pre><p><code>time</code>、<code>memory</code>、<code>status.description</code> はすべて API 応答から受信された後、<code>outputDetails</code> に保存され表示されます。</p><h3 id="--9">キーボードイベント</h3><p>最後は、<code>ctrl+enter</code> を使って、コードをコンパイルする機能です。これには、Web アプリケーションでさまざまなキーボードイベントのイベントリスナーの役割をする、カスタムフックを作成します (カスタムフックは非常に便利で、コードもきれいに保てます)。</p><pre><code class="language-javascript">// useKeyPress.js

import React, { useState } from "react";

const useKeyPress = function (targetKey) {
  const [keyPressed, setKeyPressed] = useState(false);

  function downHandler({ key }) {
    if (key === targetKey) {
      setKeyPressed(true);
    }
  }

  const upHandler = ({ key }) =&gt; {
    if (key === targetKey) {
      setKeyPressed(false);
    }
  };

  React.useEffect(() =&gt; {
    document.addEventListener("keydown", downHandler);
    document.addEventListener("keyup", upHandler);

    return () =&gt; {
      document.removeEventListener("keydown", downHandler);
      document.removeEventListener("keyup", upHandler);
    };
  });

  return keyPressed;
};

export default useKeyPress;
</code></pre><pre><code class="language-javascript">// Landing.js

...
...
...
const Landing = () =&gt; {
    ...
    ...
      const enterPress = useKeyPress("Enter");
      const ctrlPress = useKeyPress("Control");
   ...
   ...
}
</code></pre><p>ここでは、純粋な JavaScript の <code>Event Listeners</code> を使用して、目的の <code>target</code> キーのイベントを待ち、応答します。</p><p>この <code>Hook</code> は、<code>keydown</code> と <code>keyup</code> のイベントを待ちます。<code>Enter</code> と <code>Control</code> のターゲットキーを使用してフックを初期化します。</p><p><code>targetKey === key</code> かどうかを確認し、それに応じて <code>keyPressed</code> を設定しているため、<code>keyPressed</code> で返されたブール値 (true または false) を使用することができます。</p><p>これで、<code>useEffect</code> フックでこれらのイベントにリスナーを付け、両方が同時に押されたことを確認できます。</p><pre><code class="language-javascript">useEffect(() =&gt; {
    if (enterPress &amp;&amp; ctrlPress) {
      console.log("enterPress", enterPress);
      console.log("ctrlPress", ctrlPress);
      handleCompile();
    }
  }, [ctrlPress, enterPress]);
</code></pre><p>これにより、ユーザーが <code>control</code> キーと <code>enter</code> キーを連続して押すか、同時に押すたびに、<code>handleCompile()</code> メソッドが呼び出されます。</p><h2 id="--10"><strong>注意事項</strong></h2><p>楽しいプロジェクトではありますが、Judge0 の基本プランには、1 日 100 回までのリクエスト制限があります。</p><p>その対策法として、独自のサーバーやドロップレットを (Digital Ocean 上で) 起動し、オープンソースプロジェクトを独自にホストすることができます (これについての素晴らしいドキュメントも用意されています)。</p><h2 id="--11"><strong>まとめ</strong></h2><p>最終結果は以下になります。</p><ul><li>40 以上の言語でコンパイルできるコードエディター</li><li>コードエディターのデザインを変更するためのテーマ切り替え</li><li>RapidAPI 上での API の相互作用とホスティング</li><li>カスタムフックを使用した React でキーボードイベントの使用</li><li>楽しい時間！ ;)</li></ul><p>最後に、プロジェクトをさらに掘り下げたい方は、以下の機能を追加してみてはどうでしょうか。</p><ul><li>ログインおよび登録モジュール: コードを自分の個人ダッシュボードに保存できるようにします。</li><li>インターネット上で他の人とコードをシェアする方法</li><li>プロフィールページとカスタマイズ</li><li>Socket プログラミングと操作変換を使用した、単一のコードスニペットでのペアプログラミング</li><li>お気に入りのコードスニペットをブックマークする</li><li>CodePen のような、保存されたコードスニペットのカスタムダッシュボード</li></ul><p>私にとって、このアプリケーションをゼロからコーディングすることはとても楽しく、TailwindCSS は私のお気に入りであり、アプリケーションのスタイリングには欠かせないリソースです。</p><p>もしこの記事が役に立ったのなら、<a href="https://github.com/manuarora700/react-code-editor">GitHub Repository</a> に⭐️をつけていただけると嬉しいです。<br>質問があれば、<a href="https://twitter.com/mannupaaji">Twitter</a> や <a href="https://manuarora.in/">Website</a> からお気軽にご連絡ください。喜んでサポートさせていただきます。</p><p><a href="https://github.com/manuarora700/react-code-editor">Source Code</a> | <a href="https://coderush.vercel.app/">Live Demo</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript を使ったドロップダウンメニューを作成する方法 ]]>
                </title>
                <description>
                    <![CDATA[ インターネットを使うかたなら、おそらくドロップダウンメニューを使ったことがあるでしょう。ドロップダウンメニューは主に 2 つの目的で使用されます。1 つはウェブフォームでユーザーの入力を収集するため、もう 1 つはウェブアプリケーションにアクションやナビゲーションメニューを実装するためです。 ドロップダウンは、似たようなの要素の集まりに対して多数のオプションを提供する最良の方法の 1 つです。これにより、アプリケーションの一般的なレイアウトの流れを崩さずに多くの選択肢を提供することができます。ウェブアプリ以外にも、ドロップダウンメニューはスタンドアロンのソフトウェアやオペレーティングシステムなどでも使用されています。 このガイドでは、HTML、CSS、JavaScript を使用してドロップダウンナビゲーションメニューを作成する方法を学びます。 以下は、これから作成するもののスクリーンショットです。このガイドの最後に、CodePen ファイルを提供しているので、それを使って実際に試してみることができます。 ドロップダウンメニューの最終成果物ドロップダウンメニューの基礎について ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/how-to-build-a-dropdown-menu-with-javascript/</link>
                <guid isPermaLink="false">64a846edfbe7fd06692b04de</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Saki Basken ]]>
                </dc:creator>
                <pubDate>Thu, 13 Jul 2023 23:30:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2023/07/how-to-build-a-dropdown-menu-with-javascript.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/how-to-build-a-dropdown-menu-with-javascript/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Build a Dropdown Menu with JavaScript</a>
      </p><h3 id="-2-1-1-">インターネットを使うかたなら、おそらくドロップダウンメニューを使ったことがあるでしょう。ドロップダウンメニューは主に 2 つの目的で使用されます。1 つはウェブフォームでユーザーの入力を収集するため、もう 1 つはウェブアプリケーションにアクションやナビゲーションメニューを実装するためです。</h3><p></p><p>ドロップダウンは、似たようなの要素の集まりに対して多数のオプションを提供する最良の方法の 1 つです。これにより、アプリケーションの一般的なレイアウトの流れを崩さずに多くの選択肢を提供することができます。ウェブアプリ以外にも、ドロップダウンメニューはスタンドアロンのソフトウェアやオペレーティングシステムなどでも使用されています。</p><p>このガイドでは、HTML、CSS、JavaScript を使用してドロップダウンナビゲーションメニューを作成する方法を学びます。</p><p>以下は、これから作成するもののスクリーンショットです。このガイドの最後に、CodePen ファイルを提供しているので、それを使って実際に試してみることができます。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/07/dropdown-menu-with-css.png" class="kg-image" alt="dropdown-menu-with-css" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/07/dropdown-menu-with-css.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2023/07/dropdown-menu-with-css.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2023/07/dropdown-menu-with-css.png 1055w" sizes="(min-width: 720px) 720px" width="1055" height="386" loading="lazy"><figcaption>ドロップダウンメニューの最終成果物</figcaption></figure><p>ドロップダウンメニューの基礎についての説明を終えたので、次はその作成手順について説明します。</p><h2 id="-1-">ステップ 1 – ドロップダウンのマークアップを追加する</h2><p>このガイドではアイコンを使用するため、まず初めにアイコンをインポートしなければなりません。その分かりやすさから、<a href="https://boxicons.com/">Boxicons</a> という無料のライブラリを使用します。他にもあるので、ぜひお好きなライブラリを使ってみて下さい。</p><p>Boxicons を<a href="https://boxicons.com/usage">設定する</a>方法はいくつかありますが、最も簡単な方法は、次のように HTML ファイルの <code>head</code> で script タグを定義することです。</p><pre><code class="language-html">&lt;head&gt;
   &lt;link 
     href="https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css" 
     rel="stylesheet"
    /&gt;
 &lt;/head&gt;</code></pre><p>アイコンをインポートした後、クラス名が <code>container</code> の <code>div</code> 要素を作成します。この要素内に、ボタンとドロップダウンメニューを入れていきます。</p><p>コンテナ内でボタン要素を作成し、それに <code>btn</code> というクラスと ID を与えます。そのボタンに、テキストと矢印アイコンを渡します。</p><p>ボタンのマークアップは以下のとおりです。</p><pre><code class="language-html">&lt;button class="btn" id="btn"&gt;
  Dropdown
  &lt;i class="bx bx-chevron-down" id="arrow"&gt;&lt;/i&gt;
&lt;/button&gt;</code></pre><p>次に、ドロップダウンメニュー自体のマークアップを追加します。button タグの下に <code>div</code> 要素を作成し、それに <code>dropdown</code> というクラスと ID を与えます。div 要素内で、個々のドロップダウン項目ごとに <code>a</code> タグを作成し、それぞれのアイコンとテキストを渡します。</p><p>マークアップは以下のようになります。</p><pre><code class="language-html">&lt;div class="dropdown" id="dropdown"&gt;
  &lt;a href="#create"&gt;
    &lt;i class="bx bx-plus-circle"&gt;&lt;/i&gt;
    Create New
  &lt;/a&gt;
  &lt;a href="#draft"&gt;
    &lt;i class="bx bx-book"&gt;&lt;/i&gt;
    All Drafts
  &lt;/a&gt;
  &lt;a href="#move"&gt;
    &lt;i class="bx bx-folder"&gt;&lt;/i&gt;
    Move To
  &lt;/a&gt;
  &lt;a href="#profile"&gt;
    &lt;i class="bx bx-user"&gt;&lt;/i&gt;
    Profile Settings
  &lt;/a&gt;
  &lt;a href="#notification"&gt;
    &lt;i class="bx bx-bell"&gt;&lt;/i&gt;
    Notification
  &lt;/a&gt;
  &lt;a href="#settings"&gt;
    &lt;i class="bx bx-cog"&gt;&lt;/i&gt;
    Settings
  &lt;/a&gt;
&lt;/div&gt;</code></pre><p>出力は以下のようになります。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/07/dropdown-menu-markup.png" class="kg-image" alt="dropdown-menu-markup" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/07/dropdown-menu-markup.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2023/07/dropdown-menu-markup.png 772w" sizes="(min-width: 720px) 720px" width="772" height="144" loading="lazy"><figcaption>ドロップダウンメニューのマークアッププレビュー</figcaption></figure><p>このままではまだ見栄えがよくないので、メニューのスタイリングをしましょう。</p><h2 id="-2-">ステップ 2 – ドロップダウンメニューのスタイルを設定する</h2><p>まず、ページ上のすべての要素のデフォルトのマージンとパディングをリセットし、いくつかの値を変数に保存して、CSS ファイル全体で再利用できるようにします。次に、body 要素にグローバルなスタイルを適用します。</p><pre><code class="language-css">@import url(https://fonts.googleapis.com/css?family=Inter:100,200,300,regular,500,600,700,800,900);

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Inter", sans-serif;
  --shadow: rgba(0, 0, 0, 0.05) 0px 6px 10px 0px,
    rgba(0, 0, 0, 0.1) 0px 0px 0px 1px;
  --color: #166e67;
  --gap: 0.5rem;
  --radius: 5px;
}

body {
  margin: 2rem;
  background-color: #b3e6f4;
  font-size: 0.9rem;
  color: black;
}</code></pre><p>次のステップでは、ボタンとドロップダウンコンテナー自体のスタイリングを行います。作業をスピードアップするため、重要なスタイリングの部分のみを説明します。</p><p>以下のマークアップをコピーして、CSS ファイルに貼り付けてください。</p><pre><code class="language-css">.btn {
  background-color: white;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  column-gap: var(--gap);
  padding: 0.6rem;
  cursor: pointer;
  border-radius: var(--radius);
  border: none;
  box-shadow: var(--shadow);
  position: relative;
}

.bx {
  font-size: 1.1rem;
}

.dropdown {
  position: absolute;
  width: 250px;
  box-shadow: var(--shadow);
  border-radius: var(--radius);
  margin-top: 0.3rem;
  background: white;
}

.dropdown a {
  display: flex;
  align-items: center;
  column-gap: var(--gap);
  padding: 0.8rem 1rem;
  text-decoration: none;
  color: black;
}

.dropdown a:hover {
  background-color: var(--color);
  color: white;
}</code></pre><p>ドロップダウンメニューは通常、要素の上に重なるように表示されるため、ボタンを相対的 (relative) に、ドロップダウンメニューを絶対的 (absolute) に配置しています。これにより、両方の要素が近くに配置され、ドロップダウンメニューが他の要素の上に表示されることが保証されます。こうすることで、切り替え時にページのフローに影響を与えなくなります。</p><p>出力は以下のようになります。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/07/dropdown-menu-with-css--1-.png" class="kg-image" alt="dropdown-menu-with-css--1-" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/07/dropdown-menu-with-css--1-.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2023/07/dropdown-menu-with-css--1-.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2023/07/dropdown-menu-with-css--1-.png 1055w" sizes="(min-width: 720px) 720px" width="1055" height="386" loading="lazy"><figcaption>ドロップダウンメニューのスタイリング</figcaption></figure><p>ドロップダウンのスタイルが設定されたので、即座に表示されるのではなく、ボタンがクリックされたときにのみドロップダウンが表示されるようにしたいと思います。非表示にするには CSS を使用します。</p><p>以前 <a href="https://freecodecamp.org/news/how-to-build-a-modal-with-javascript">JavaScript でモーダルを作成する方法</a>という記事では、最初にビューポートからモーダル要素を非表示にするために、<code>display: none</code> を使用しました。しかし、<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties">MDN Docs</a> によると、このプロパティを利用する場合の欠点は、アニメーション化できないことです。</p><p>したがってこのガイドでは、別の方法を用いてドロップダウンメニューを非表示にしたいと思います。これには、<code>visibility</code> と <code>opacity</code> のプロパティを組み合わせることで、達成することができます。そしてこの方法は、<a href="https://github.com/">GitHub</a> がドロップダウンメニューを実装している方法です。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/07/github-dropdown-menu.png" class="kg-image" alt="github-dropdown-menu" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/07/github-dropdown-menu.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2023/07/github-dropdown-menu.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2023/07/github-dropdown-menu.png 1304w" sizes="(min-width: 720px) 720px" width="1304" height="575" loading="lazy"><figcaption>GitHub のドロップダウンメニュー</figcaption></figure><p>前に作成したドロップダウンクラス内に、visibility プロパティを追加し、値を hidden に設定したら、不透明度を <code>0</code> に設定します。そうすることで、ページからドロップダウンメニューが非表示になります。</p><p>ドロップダウンメニューを表示するために、<code>show</code> という別のクラスを作成します。このクラスは、<code>visible</code> の値と不透明度 <code>1</code> の visibility プロパティを保持します。そして、後ほど JavaScript を使用してこのクラスをドロップダウンメニューに追加します。</p><p>コードは以下のとおりです。</p><pre><code class="language-css">.dropdown {
  position: absolute;
  width: 250px;
  box-shadow: var(--shadow);
  border-radius: var(--radius);
  margin-top: 0.3rem;
  background: white;
  transition: all 0.1s cubic-bezier(0.16, 1, 0.5, 1);
    
  transform: translateY(0.5rem);
  visibility: hidden;
  opacity: 0;
}

.show {
  transform: translateY(0rem);
  visibility: visible;
  opacity: 1;
}

.arrow {
  transform: rotate(180deg);
  transition: 0.2s ease;
}</code></pre><p>ドロップダウンメニューの要素を非表示にするスタイルに加えて、ドロップダウンボタンをクリックしたときに矢印アイコンを回転させる、別のクラスも追加しました。</p><h2 id="-3-">ステップ 3 – ドロップダウン機能を追加する</h2><p>まず、それぞれの要素を変数に保存して再利用できるようにしましょう。</p><pre><code class="language-js">const dropdownBtn = document.getElementById("btn");
const dropdownMenu = document.getElementById("dropdown");
const toggleArrow = document.getElementById("arrow");</code></pre><p>次のステップでは、ボタンがクリックされたときにドロップダウン要素の <code>show</code> というクラスの切り替えと、ドロップダウンの矢印の回転を行う関数を作成します。この関数の名前を <code>toggleDropdown</code> とします。</p><pre><code class="language-js">const toggleDropdown = function () {
  dropdownMenu.classList.toggle("show");
  toggleArrow.classList.toggle("arrow");
};</code></pre><p>そして、<code>addEventListener</code> メソッドを使用して、ドロップダウンボタンでこの関数を呼び出すことができます。そうすれば、ボタンをクリックするたびにドロップダウンメニューの表示と非表示を制御する関数が起動されます。</p><pre><code class="language-js">dropdownBtn.addEventListener("click", function (e) {
  e.stopPropagation();
  toggleDropdown();
});</code></pre><p>もうお気づきかもしれませんが、ドロップダウン関数内に <code>stopPropagation()</code> メソッドを追加しました。これにより、ボタン要素の機能が親要素に伝達されるのを防ぎ、関数が 2 回実行されるのを防ぐことができます。次のセクションでこれについて詳しく説明します。</p><p>出力は以下のようになります。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/07/toggle-dropdown-menu-.gif" class="kg-image" alt="toggle-dropdown-menu-" width="938" height="373" loading="lazy"><figcaption>ドロップダウンメニューの切り替え</figcaption></figure><h2 id="dom-">DOM 要素をクリックしたときにドロップダウンメニューを閉じる方法</h2><p>ドロップダウンメニューは通常、次の 4 つの異なる方法で閉じられます。</p><ul><li>ドロップダウンメニューを有効にするボタンをクリックすること</li><li>子要素のいずれかをクリックすること</li><li>メニューの外側 (ボディ部分) をクリックすること</li><li>Esc キーまたは下矢印キーを押すこと</li></ul><p>ただし、このガイドでは、最初の 3 つに焦点を当てましょう。</p><p>まず、<code>document.documentElement</code> を使用してルート要素 <code>&lt;html&gt;</code> を選択します。そうしたら、先ほどと同様に <code>toggleDropdown()</code> 関数を渡します。</p><p>しかし、今回はドロップダウンメニューが <code>show</code> というクラスを持っているかどうかをチェックする条件を設定したいと思います。<code>show</code> というクラスを持っている場合にのみ、ドロップダウンメニューを閉じる関数を実行したいからです。</p><pre><code class="language-js">document.documentElement.addEventListener("click", function () {
  if (dropdownMenu.classList.contains("show")) {
    toggleDropdown();
  }
});</code></pre><p>その結果が、最終的にこのようになります。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/07/close-dropdown-when-dom-element-is-clicked.gif" class="kg-image" alt="close-dropdown-when-dom-element-is-clicked" width="904" height="349" loading="lazy"><figcaption>DOM 要素をクリックするとドロップダウンが閉じる</figcaption></figure><p>これが JavaScript でドロップダウンメニューを作成する方法です。以下は、このドロップダウンメニューをテストするための CodePen ファイルです。</p><figure class="kg-card kg-embed-card"><iframe height="400" scrolling="no" title="Embedded content" src="https://codepen.io/evavic44/embed/eYKQJjJ?default-tab=html%2Cresult&amp;theme-id=light" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-variant-alternates: inherit; font-weight: 400; font-stretch: inherit; line-height: inherit; font-family: Lato, sans-serif; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 22px; vertical-align: middle; color: rgb(10, 10, 35); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; width: 720px;"></iframe></figure><h2 id="-">まとめ</h2><p>この投稿が興味深かったり、役に立ったと感じていただければ幸いです。今後の投稿を見逃さないよう、お友達と共有するか、私のブログを読者登録してみてください。読んでくださりありがとうございます。</p><p><a href="https://github.com/evavic44">GitHub</a> | <a href="https://twitter.com/victorekea">Twitter</a> | <a href="https://eke.hashnode.dev/">Blog</a> | <a href="https://victoreke.com/">Portfolio</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Python で文字列を追加する – STR 追加 ]]>
                </title>
                <description>
                    <![CDATA[ この記事では、Python で文字列を追加するさまざまな方法を学びます。 文字列の追加について話すときによく使用されるもう 1 つの用語は、連結です。そのため、追加と連結という用語が同じ意味で使用されることがよくあります。 いずれにしても、文字列を追加または連結するとは、ある文字列の値を別の文字列に追加または結合することを意味します。 簡単なコード例を使って、文字列追加のさまざまな方法を見ていきましょう。 + 演算子を使用して Python で文字列を追加する方法 + 演算子を使用して 2 つ以上の文字列を結合できます。例えば、以下のようにすることができます。 first_name = "John" last_name = "Doe" print(first_name + last_name) # JohnDoe 上の例では、first_name と last_name という 2 つの文字列変数を作成しました。それぞれ「John」と「Doe」という値を持ちます。 これらの変数を追加するには、first_name + last_name のように、+ 演算子を使用しまし ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/append-a-string-in-python-str-appending/</link>
                <guid isPermaLink="false">647f6fcf60403f05988fc265</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Saki Basken ]]>
                </dc:creator>
                <pubDate>Fri, 09 Jun 2023 09:07:59 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2023/06/alex-chumak-zGuBURGGmdY-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/append-a-string-in-python-str-appending/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Append a String in Python – STR Appending</a>
      </p><p>この記事では、Python で文字列を追加するさまざまな方法を学びます。</p><p>文字列の追加について話すときによく使用されるもう 1 つの用語は、連結です。そのため、追加と連結という用語が同じ意味で使用されることがよくあります。</p><p>いずれにしても、文字列を追加または連結するとは、ある文字列の値を別の文字列に追加または結合することを意味します。</p><p>簡単なコード例を使って、文字列追加のさまざまな方法を見ていきましょう。</p><h2 id="-python-"><strong><code>+</code></strong> 演算子を使用して Python で文字列を追加する方法</h2><p><code>+</code> 演算子を使用して 2 つ以上の文字列を結合できます。例えば、以下のようにすることができます。</p><pre><code class="language-python">first_name = "John"
last_name = "Doe"

print(first_name + last_name)
# JohnDoe</code></pre><p>上の例では、<code>first_name</code> と <code>last_name</code> という 2 つの文字列変数を作成しました。それぞれ「John」と「Doe」という値を持ちます。</p><p>これらの変数を追加するには、<code>first_name + last_name</code> のように、<code>+</code> 演算子を使用しました。</p><p>出力された <code>JohnDoe</code> から、2 つの変数がスペースを入れずに結合されたことがわかります。</p><p>"John " のように、<code>first_name</code> の値の後にスペースを追加することができます。または、" Doe" のように、<code>last_name</code> の値の前にスペースを追加することもできます。すると、以下のようになります。</p><pre><code class="language-python">first_name = "John "
last_name = "Doe"

print(first_name + last_name)
# John Doe</code></pre><p>文字列を追加するときに引用符を使用して、次のとおりにスペースを追加することもできます。</p><pre><code class="language-python">first_name = "John "
last_name = "Doe"

print(first_name + "" + last_name)
# John Doe</code></pre><h2 id="join-python-"><strong><code>join()</code></strong> メソッドを使用して Python で文字列を追加する方法</h2><p>Python で文字列を追加するもう 1 つの方法は、<code>join()</code> メソッドを使用することです。</p><p> <code>join()</code> メソッドは、反復可能なオブジェクト (リスト、タプル、文字列、セット、ディクショナリー) をパラメーターとして受け取ります。構文は次のようになります。</p><pre><code class="language-python">string.join(iterable_object)</code></pre><p>以下は、<code>join()</code> メソッドを使用して文字列を追加する方法の一例です。</p><pre><code class="language-python">first_name = "John"
last_name = "Doe"

print("".join([first_name, last_name]))
# JohnDoe</code></pre><p>ここでは、2 つの文字列変数をパラメーターとして <code>join()</code> メソッドに渡します。</p><p>また、変数が角括弧 <code>[]</code> でネストされており、文字列のリスト <code>[first_name, last_name]</code> になっていることがわかります。これは、<code>join()</code> メソッドが 1 つのパラメーターしか受け取らず、そのパラメーターは反復可能なオブジェクトである必要があるためです。</p><p><code>join()</code> メソッドの一つ不思議な点は、ピリオド (ドット) の前にくる引用符です。</p><p>これらの引用符を使用して、反復可能なオブジェクトの値の間に表示される内容を指定することができます。例を挙げて説明しましょう。</p><pre><code class="language-python">first_name = "John"
last_name = "Doe"

print("#".join([first_name, last_name]))
# John#Doe</code></pre><p>上の例では、<code>"#".join([first_name, last_name])</code> のように、<code>#</code> 記号を引用符に追加しました。この <code>#</code> は、<code>John#Doe</code> でわかるように、文字列の間に追加されました。</p><p>前のセクションでは、文字列間にスペースを追加するためにさまざまな方法を使用する必要がありました。<code>join()</code> メソッドの前にある引用符の中にスペースを追加することで、これを簡単に実現できます。</p><pre><code class="language-python">first_name = "John"
last_name = "Doe"

print(" ".join([first_name, last_name]))
# John Doe</code></pre><h2 id="string-format-python-">String<strong> <code>format()</code> </strong>メソッドを使用して Python で文字列を追加する方法</h2><p>string <code>format()</code> メソッドの構文は次のようになります。</p><pre><code class="language-txt">{}.format(value)</code></pre><p>基本的に、文字列の format メソッドは、上記の構文で指定した値のパラメーターを受け取り、それを波括弧の中に挿入します。結果として得られる値は文字列となります。</p><p>以下はその例になります。</p><pre><code class="language-python">first_name = "John"
last_name = "Doe"

print("{} {}".format(first_name, last_name))
# John Doe</code></pre><p>この例では、2 つの波括弧と 2 つのパラメーター (<code>first_name</code> and <code>last_name</code>) を指定したため、string <code>format()</code> メソッドは文字列をそれぞれの波括弧に挿入します。</p><p>波括弧のある引用符内には、さらに文字列を追加することができます。これによって string <code>format()</code> メソッドの処理機能が影響されることはありません。それらの文字列は依然として波括弧に挿入されます。つまり、こうなります。</p><pre><code class="language-python">first_name = "John"
last_name = "Doe"

print("My name is {} {}".format(first_name, last_name))
# My name is John Doe</code></pre><h2 id="f-string-python-"><strong>f-string</strong> を使用して Python で文字列を追加する方法</h2><p>この方法は比較的、理解しやすいものです。f-string は、文字列の書式設定と補間を容易にするために Python に導入されましたが、文字列を追加するために使用することもできます。</p><p>f-string を使用するには、<code>f""</code> のように、f の後に引用符を続けるだけです。そうすると、引用符の間に文字列と変数名を挿入できます。変数名はすべて波括弧で囲む必要があります。</p><p>以下の例をご覧ください。</p><pre><code class="language-python">first_name = "John"
last_name = "Doe"

print(f"{first_name} {last_name}")
# John Doe</code></pre><h2 id="-">まとめ</h2><p>この記事では、Python で文字列を追加するために使用できるさまざまな方法について説明しました。</p><p>文字列を別の文字列に追加するということは、文字列を結合することを意味します。</p><p>この記事とコード例で説明したように、Python では <code>+</code> 演算子、<code>join()</code> メソッド、<code>string()</code> フォーマットメソッド、および f-string を使用して文字列を追加できます。</p><p>Happy coding!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 初心者向け React フックの基礎 ]]>
                </title>
                <description>
                    <![CDATA[ React.js は、オープンソースの JavaScript ベースのユーザーインターフェイスライブラリーです。ウェブおよびモバイルアプリの開発で非常に人気があります。 React はコンポーネントベースの構成を原則としています。React のコンポーネント は、それぞれ独立していて、再利用可能なコード部分のことを指します。コンポーネントには、クラスコンポーネントと関数コンポーネントの 2 つのタイプがあります。 React バージョン 16.8 までは、開発者はクラスコンポーネントを使用してのみ、state などの React の機能を扱うことができました。しかし、バージョン 16.8 で、React にフックと呼ばれる新しいパターンが導入されました。 React のフックを使用することで、関数コンポーネントで state を含めさまざまな React の機能を使用することができます。これにより、開発者は React で関数型プログラミングを実行できるようになりました。 この記事では、React フックについての基礎を学んでいきます。この記事を書く背景には、「React のフ ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/react-hooks-fundamentals/</link>
                <guid isPermaLink="false">6463b0d4e981370655a5ff10</guid>
                
                    <category>
                        <![CDATA[ React Hooks ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Saki Basken ]]>
                </dc:creator>
                <pubDate>Wed, 24 May 2023 13:40:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2023/05/freeCodeCamp-Cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/react-hooks-fundamentals/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">React Hooks Fundamentals for Beginners</a>
      </p><p>React.js は、オープンソースの JavaScript ベースのユーザーインターフェイスライブラリーです。ウェブおよびモバイルアプリの開発で非常に人気があります。</p><p>React は<code>コンポーネントベース</code>の構成を原則としています。React の<code>コンポーネント</code>は、それぞれ独立していて、再利用可能なコード部分のことを指します。コンポーネントには、クラスコンポーネントと関数コンポーネントの 2 つのタイプがあります。</p><p>React バージョン 16.8 までは、開発者はクラスコンポーネントを使用してのみ、state などの React の機能を扱うことができました。しかし、バージョン 16.8 で、React に<code>フック</code>と呼ばれる新しいパターンが導入されました。</p><p>React のフックを使用することで、関数コンポーネントで state を含めさまざまな React の機能を使用することができます。これにより、開発者は React で関数型プログラミングを実行できるようになりました。</p><p>この記事では、<code>React フック</code>についての基礎を学んでいきます。この記事を書く背景には、「React のフックは学びやすく、作成や使用も簡単」という考え方を初心者の方々に持ってもらいたいという思いがあります。基礎さえしっかり理解していれば、本当に簡単なのです。</p><p>もし動画で学ぶことがお好きなら、この記事の内容はビデオチュートリアルでもご利用いただけます。ぜひこちらからご覧ください🙂</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/CvNvRaS3u60?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="06 - What are React Hooks - An Introduction to React Hooks - Functional Components - ReactJS" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><h2 id="-">フックについて学ぶ前に</h2><p>フックの前に、まず基本の <code>JavaScript の関数</code> (バニラ JS の関数) について考えてみましょう。</p><p>JavaScript というプログラミング言語では、関数は繰り返し行うタスクのための再利用可能なコードロジックです。関数はコンポーザブル (合成可能) です。つまり、関数を別の関数内で呼び出してその出力を使用することができます。</p><p>以下の図では、関数 <code>someFunction()</code> が関数 <code>a()</code> と <code>b()</code> をコンポーズ (合成使用) しています。<code>b()</code> の関数は関数 <code>c()</code> を使用しています。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/05/some-function.png" class="kg-image" alt="some-function" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/05/some-function.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2023/05/some-function.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2023/05/some-function.png 1253w" sizes="(min-width: 720px) 720px" width="1253" height="660" loading="lazy"><figcaption>関数のコンポーザビリティー (合成可能性)</figcaption></figure><p>これをコードで書くと、次のようになります。</p><pre><code class="language-js">function a() {
    // some code
}

function c() {
    // some code
}

function b() {
    // some code
    
    c();
    
    // some code
}

function someFunction() {
    // some code
    
	a();
    b();
    
    // some code
}</code></pre><p>React の関数コンポーネントが単なるバニラ JavaScript の関数であることは周知の事実です。したがって、関数が合成可能な場合、React コンポーネントも<br>合成可能な性質を持つことになります。以下の図のように、これは 1 つ以上のコンポーネントを別のコンポーネントに合成使用できることを意味します。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/05/Component-A.png" class="kg-image" alt="Component-A" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/05/Component-A.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2023/05/Component-A.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2023/05/Component-A.png 1248w" sizes="(min-width: 720px) 720px" width="1248" height="650" loading="lazy"><figcaption>コンポーネントのコンポーザビリティー (合成可能性)</figcaption></figure><h2 id="--1">ステートフルコンポーネントとステートレスコンポーネント</h2><p>React のコンポーネントには、state を持つものと、持たないものがあります。</p><ul><li>state を持つコンポーネントは、その中で状態を確立し管理します。</li><li>state を持たないコンポーネントは、状態や副作用の管理をしない、純粋な関数です。</li></ul><p><a href="https://blog.greenroots.info/what-are-pure-functions-and-side-effects-in-javascript">純粋関数</a>とは、副作用のない関数のことです。つまりこれは、関数が同じ入力に対して常に同じ出力を返す、ということを意味します。</p><p>関数コンポーネントから、state や副作用のロジックを取り除けば、state を持たないステートレスコンポーネントになります。また、取り除いた state を持つロジックや副作用のロジックは、アプリ内の他の場所で再利用することもできます。そのため、それらをできる限りコンポーネントから隔離することが、合理的なのです。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/05/Stateless-Component.png" class="kg-image" alt="Stateless-Component" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/05/Stateless-Component.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2023/05/Stateless-Component.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2023/05/Stateless-Component.png 1251w" sizes="(min-width: 720px) 720px" width="1251" height="647" loading="lazy"><figcaption>状態に関するロジックを持つ、ステートフルコンポーネント</figcaption></figure><h2 id="react-">React フックとステートフルロジック</h2><p>React フックを使用することで、ステートフルロジックと副作用を関数コンポーネントから切り離すことができます。フックとは、状態の変化や副作用をコンポーネントから切り離して管理することができる、JavaScript の関数のことです。</p><p>これにより、すべてのステートフルロジックをフックとして分離し、他のコンポーネントに使用できるようになるのです。(フックも関数であるため、合成可能。)</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/05/stateless-component.png" class="kg-image" alt="stateless-component" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/05/stateless-component.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2023/05/stateless-component.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2023/05/stateless-component.png 1531w" sizes="(min-width: 720px) 720px" width="1531" height="663" loading="lazy"><figcaption>ステイトフルロジックをフックとして切り離した状態</figcaption></figure><p>では、このステートフルロジックとは何なのでしょうか？状態変数をローカルで宣言および管理しているものであれば、何でも当てはまります。</p><p>たとえば、データをフェッチし、ローカル変数でデータを管理するロジックは「ステートフル」ということになります。複数のコンポーネントでそのような取得ロジックを再利用すると良い場合もあります。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/05/component-abc.png" class="kg-image" alt="component-abc" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/05/component-abc.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2023/05/component-abc.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2023/05/component-abc.png 1539w" sizes="(min-width: 720px) 720px" width="1539" height="787" loading="lazy"></figure><h2 id="-react-"><strong>では、React フックとは一体なんなのでしょうか？</strong></h2><p>React フックをわかりやすい言葉で定義するとどうなるでしょうか。関数、コンポーザビリティー、コンポーネント、状態、副作用について理解した今、React フックとは何かをご説明します。</p><blockquote>React フックとは、関数コンポーネント内の再利用可能な部分を切り離して使用できるシンプルな JavaScript の関数です。フックは状態を持つことができ、副作用を管理することもできます。</blockquote><p>React は、標準の組み込みフックを多数提供しています。</p><ul><li><code>useState</code>: 状態を管理するためのフックです。状態の値と、それを更新するための更新関数を返します。</li><li><code>useEffect</code>: API 呼び出し、サブスクリプション、タイマー、ミューテイションなどのような副作用を管理するためのフックです。</li><li><code>useContext</code>: コンテキストの現在の値を返します。</li><li><code>useReducer</code>: 複雑な状態管理をサポートするための、<code>useState</code> の代替となるフックです。</li><li><code>useCallback</code>: 子コンポーネントが必要以上にレンダリングされないように、コールバックをメモ化されたものを返します。</li><li><code>useMemo</code>: パフォーマンスの最適化に役立つメモ化された値を返します。</li><li><code>useRef</code>: <code>.current</code> プロパティを持つ ref オブジェクトを返します。そのような ref オブジェクトはミュータブル (変更可能) です。これは主に子コンポーネントに直接アクセスするために使用されます。</li><li><code>useLayoutEffect</code>: すべての DOM ミューテーションの最後に始動します。<code>useLayoutEffect</code> は同期的に始動するため、できればこれよりも <code>useEffect</code> を使用する方がオススメです。</li><li><code>useDebugValue</code>: React DevTools でカスタムフックのラベルを表示するのに役立ちます。</li></ul><p>これらのフックについて詳しくは、<a href="https://ja.legacy.reactjs.org/docs/hooks-reference.html">こちら</a>から読むことができます。全てのフック名が use で始まることに注目してください。これは React コードベースでフックを素早く特定するための標準的な慣例です。</p><p>データのフェッチ、ディスクへのログ、タイマーなど、様々な用途に合わせて独自のカスタムフックを作成することもできます。</p><p>なのでもし、コードベースで React フックを見かけたり、作成するように求められた場合は、心配しないでください。それは、単に関数コンポーネントの外で状態や副作用を扱うための別の JavaScript 関数なだけなのです。</p><p>カスタムフックを作成するためのステップバイステップガイドをお探しであれば、<a href="https://blog.greenroots.info/how-to-create-a-countdown-timer-using-react-hooks">この記事</a>が役立つかもしれません。</p><h2 id="--2"><strong>最後に</strong></h2><p>この React フックの紹介が少しでもお役に立てば幸いです。React 経験も長くなり、React のあらゆる側面を網羅することを目指した YouTube 動画シリーズを始めました。もしお役に立てそうであれば、ぜひチャンネル登録をお願いします。</p><p>私は、次のプラットフォームでも JavaScript、ウェブ開発、ブログについて学んだことを共有しているので、ぜひ繋がりましょう。</p><ul><li><a href="https://twitter.com/tapasadhikary">Twitter でフォローする</a></li><li><a href="https://github.com/atapas">GitHub でサイドプロジェクトを見る</a></li></ul><p>それではまた、次の記事でお会いしましょう。それまで、心身ともに体に気をつけてお過ごしください。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript の forEach – JS の配列をループする方法 ]]>
                </title>
                <description>
                    <![CDATA[ JavaScript の forEach メソッドは、配列をループするいくつかの方法の 1 つです。それぞれの方法には独自の機能があり、目的に応じて、どの方法を使用するかを決めるのはあなた次第です。 この投稿では、JavaScript の forEach メソッドを詳しく見ていきます。 まず、以下の配列があるとします。 const numbers = [1, 2, 3, 4, 5]; 従来の「for ループ」を使用して配列をループすると、次のようになります。 for (i = 0; i < numbers.length; i++) {   console.log(numbers[i]); } forEach( ) メソッドが特別な理由。 forEach メソッドも配列をループするために使用されますが、従来の「for ループ」とは異なる方法で関数を使用します。 forEach メソッドは、コールバック関数 [https://www.freecodecamp.org/news/javascript-callback-functions-what-are-callbacks-i ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/javascript-foreach-how-to-loop-through-an-array-in-js/</link>
                <guid isPermaLink="false">644ca2d2575f110652ab43ca</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Saki Basken ]]>
                </dc:creator>
                <pubDate>Mon, 08 May 2023 13:40:16 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2023/05/5f9c99d8740569d1a4ca2204-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/javascript-foreach-how-to-loop-through-an-array-in-js/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript forEach – How to Loop Through an Array in JS</a>
      </p><p>JavaScript の forEach メソッドは、配列をループするいくつかの方法の 1 つです。それぞれの方法には独自の機能があり、目的に応じて、どの方法を使用するかを決めるのはあなた次第です。</p><p>この投稿では、JavaScript の forEach メソッドを詳しく見ていきます。</p><p>まず、以下の配列があるとします。</p><pre><code class="language-javascript">const numbers = [1, 2, 3, 4, 5];</code></pre><p>従来の「for ループ」を使用して配列をループすると、次のようになります。</p><pre><code class="language-javascript">for (i = 0; i &lt; numbers.length; i++) {
  console.log(numbers[i]);
}</code></pre><h2 id="foreach-">forEach( ) メソッドが特別な理由。</h2><p>forEach メソッドも配列をループするために使用されますが、従来の「for ループ」とは異なる方法で関数を使用します。</p><p>forEach メソッドは、<a href="https://www.freecodecamp.org/news/javascript-callback-functions-what-are-callbacks-in-js-and-how-to-use-them/">コールバック関数</a>を次のパラメーターと共に配列の各要素に渡します。</p><ul><li>現在の値 (必須) - 現在の配列要素の値 </li><li>インデックス (オプション) - 現在の要素のインデックス番号</li><li>配列 (オプション) - 現在の要素が属する配列オブジェクト</li></ul><p>これらのパラメーターを一つずつ説明していきます。</p><p>まず、forEach メソッドを使用して配列をループするには、コールバック関数 (または無名関数) が必要です。</p><pre><code class="language-javascript">numbers.forEach(function() {
    // code
});</code></pre><p>この関数は、配列のすべての要素に対して実行されます。配列の要素を少なくとも 1 つ、パラメーターとして受け取る必要があります。</p><pre><code class="language-javascript">numbers.forEach(function(number) {
    console.log(number);
});</code></pre><p>配列をループするために必要なことはこれだけです。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/05/Ads-z-2.png" class="kg-image" alt="Ads-z-2" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/05/Ads-z-2.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2023/05/Ads-z-2.png 824w" sizes="(min-width: 720px) 720px" width="824" height="348" loading="lazy"></figure><p>代わりに、コードを簡素化するために ES6 の、アロー関数表現を使用することもできます。</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">numbers.forEach(number =&gt; console.log(number));</code></pre><figcaption>アロー関数表現</figcaption></figure><h2 id="-">オプションのパラメーター</h2><h3 id="--1"><strong><strong>インデックス</strong></strong></h3><p>それでは、オプションのパラメーターも見てみましょう。1 つ目は「インデックス」パラメーターで、各要素のインデックス番号を表します。</p><p>基本的に、2 番目のパラメーターとしてインデックスを含めることで、それぞれの要素のインデックス番号を確認できます。</p><pre><code class="language-javascript">numbers.forEach((number, index) =&gt; {
    console.log('Index: ' + index + ' Value: ' + number);
});</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/05/Ads-z-3.png" class="kg-image" alt="Ads-z-3" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/05/Ads-z-3.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2023/05/Ads-z-3.png 822w" sizes="(min-width: 720px) 720px" width="822" height="328" loading="lazy"></figure><h3 id="--2">配列</h3><p>配列パラメータとは、配列そのものを表します。これもオプションであり、必要に応じてさまざまな用途で使用することができますが、基本、呼び出した場合には、配列の要素数と同じ回数だけ印刷されます。</p><pre><code class="language-javascript">numbers.forEach((number, index, array) =&gt; {
    console.log(array);
});</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/05/Ads-z.png" class="kg-image" alt="Ads-z" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/05/Ads-z.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2023/05/Ads-z.png 806w" sizes="(min-width: 720px) 720px" width="806" height="232" loading="lazy"></figure><p>このビデオで forEach( ) メソッドの使用例を見ることができます。</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.25%;" class="fluid-width-video-wrapper">
            <iframe src="https://www.youtube.com/embed/E2GawbHDFfs?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" title="Embedded content" loading="lazy" name="fitvid0" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 22px; vertical-align: middle; position: absolute; top: 0px; left: 0px; width: 720px; height: 405px;"></iframe>
          </div>
        </div>
      </figure><h2 id="--3">ブラウザーのサポート</h2><p>Array.forEach メソッドは、IE バージョン 8 以前を除くすべてのブラウザで<a href="https://caniuse.com/?search=Array.foreach">サポートされています</a>。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/05/Ads-z--1-.png" class="kg-image" alt="Ads-z--1-" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/05/Ads-z--1-.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2023/05/Ads-z--1-.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2023/05/Ads-z--1-.png 1261w" sizes="(min-width: 720px) 720px" width="1261" height="354" loading="lazy"><figcaption><a href="https://caniuse.com">caniuse.com</a></figcaption></figure><p><strong>ウェブ開発についてもっと詳しく知りたい方は、ぜひ私の </strong><a href="https://www.youtube.com/channel/UC1EgYPCvKCXFn8HlpoJwY3Q?view_as=subscriber"><strong>YouTube チャンネル</strong></a><strong>にアクセスしてみて下さい。</strong></p><p>最後まで読んでいただきありがとうございます！</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
