<?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[ React - 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[ React - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/japanese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 19 May 2026 10:02:12 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/japanese/news/tag/react/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <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[ React Hooks を使って localStorage のデータを保存・取得する方法 ]]>
                </title>
                <description>
                    <![CDATA[ localStorage は、JavaScript を用いて作られたサイトやアプリが、有効期限なしでウェブブラウザにキー・バリュー形式のデータを保存するためのウェブストレージオブジェクトです。 つまり、保存されたデータはページを更新したり、ブラウザを再起動しても残ったままです。これは、ブラウザに保存されたデータは、ブラウザウィンドウが閉じられても残ることを意味します。 要するに、localStorage を使うことで、開発者はブラウザ内にデータを保存したり取得したりできるようになります。 しかし、localStorage をプロジェクトのデータベースとして使用するのは良い方法ではありません。 localStorage は、アプリケーションにダークモード機能を追加したり、ToDo のデータを保存したり、ユーザーのフォーム入力値を永続化したり、その他多くの場面で頻繁に使用されます。 今回は、React Hooks で localStorage を使用し、データを簡単に保存・取得する方法を見ていきましょう。 React Hooks を使って localStorage へデータを保 ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/how-to-use-localstorage-with-react-hooks-to-set-and-get-items/</link>
                <guid isPermaLink="false">65034b9f0e802a03f9dba229</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React Hooks ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Yuki Shibata ]]>
                </dc:creator>
                <pubDate>Thu, 07 Dec 2023 23:49:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2023/09/Yellow-and-Purple-Geometric-Covid-19-General-Facts-Twitter-Post.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/how-to-use-localstorage-with-react-hooks-to-set-and-get-items/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Use localStorage with React Hooks to Set and Get Items</a>
      </p><p>localStorage は、JavaScript を用いて作られたサイトやアプリが、有効期限なしでウェブブラウザにキー・バリュー形式のデータを保存するためのウェブストレージオブジェクトです。</p><p>つまり、保存されたデータはページを更新したり、ブラウザを再起動しても残ったままです。これは、ブラウザに保存されたデータは、ブラウザウィンドウが閉じられても残ることを意味します。</p><p>要するに、localStorage を使うことで、開発者はブラウザ内にデータを保存したり取得したりできるようになります。</p><p>しかし、localStorage をプロジェクトのデータベースとして使用するのは良い方法ではありません。</p><p>localStorage は、アプリケーションにダークモード機能を追加したり、ToDo のデータを保存したり、ユーザーのフォーム入力値を永続化したり、その他多くの場面で頻繁に使用されます。</p><p>今回は、React Hooks で localStorage を使用し、データを簡単に保存・取得する方法を見ていきましょう。</p><h3 id="react-hooks-localstorage-scrim-"><strong>React Hooks を使って localStorage へデータを保存・取得する方法についてのインタラクティブな Scrim:</strong></h3><p>(訳注: Scrimba というサイトの機能で、インタラクティブなエディタと動画がセットになっています。)</p><!--kg-card-begin: html--><iframe src="https://scrimba.com/scrim/crdLpnSG?embed=freecodecamp,mini-header" width="100%" height="480" title="Embedded content" loading="lazy"></iframe><!--kg-card-end: html--><h3 id="react-hooks-"><strong>React Hooks とは</strong></h3><p>React Hooks は、コンポーネントに機能を追加するために React から import する JavaScript の関数です。</p><p>Reat Hooks を使うことで、関数コンポーネント内で state やライフサイクルメソッドを使うことができます。また、既存のコードでも動作するため、導入も簡単です。</p><p>React Hooks を用いて localStorage を使用するには、2 つの React Hooks を使います: </p><ul><li><code>useState()</code> – アプリケーションの state は、いつか必ず変化します。<code>useState()</code> は、初期 state (変数やオブジェクト、あるいはその他のデータ型) を 1 つの引数として受け取り、現在の state とその state の更新関数を返します。</li><li><code>useEffect()</code> – この React Hooks は、デフォルトでは最初のレンダリングの後、state が変更されるたびに実行されます。名前が示すように、state が変更されるたびに副作用 (effect) を実行するために使用されます。この React Hooks は、イベントリスナーを設定したり、API からデータを取得したり、コンポーネントが DOM から削除される前にイベントリスナーを削除したりするのに最適です。</li></ul><h3 id="react-localstorage-"><strong>React で localStorage を操作する方法</strong></h3><p>ブラウザの localStorage にアクセスするためのメソッドは 5 つあります:</p><ul><li><code>setItem()</code> : キー・バリュー形式のデータを localStorage に保存します。</li><li><code>getItem()</code> : データを保存した時のキーを指定して localStorage から値を取得します。</li><li><code>removeItem()</code> : データを保存した時のキーを指定して localStorage から値を削除します。</li><li><code>clear()</code> : localStorage に保存されている全てのデータを削除します。</li><li><code>key(index)</code>: localStorage に保存されている、指定された index 番号のキー名を返します。</li></ul><p>今回は、最も使用頻度の高い <code>setItem()</code> と <code>getItem()</code> に絞って解説していきます。</p><h3 id="setitem-"><strong><code>setItem()</code> の使い方</strong></h3><p>localStorage にキーを設定すると、データを格納できます。このデータの値は、テキスト、整数、オブジェクト、配列など、どのようなデータ型でも構いません。</p><p>localStorage にデータを格納する際、そのデータを <code>JSON.stringify()</code> を使って文字列へ変換する必要があることを忘れないでください。</p><pre><code class="language-react">const [items, setItems] = useState([]);

useEffect(() =&gt; {
  localStorage.setItem('items', JSON.stringify(items));
}, [items]);</code></pre><p>上記のコードでは、まず空配列の state を作成 (配列以外のデータ型でも良い) しています。次に、<code>useEffect()</code> を使うことによって state が更新されたときに localStorage へデータを追加しています。これは <code>useEffect()</code> の第二引数に state を渡すことで実現しています。</p><p>基本的には、以下のように指定することで localStorage にキー・バリュー形式のデータを追加できます:</p><pre><code class="language-react">localStorage.setItem('items', JSON.stringify(items));</code></pre><p>つまり、先ほどのコードではキーに 'items' という名前を付け、それに値を代入しています。ただし、まず追加するデータが JSON 文字列であることを確認する必要がありましたね。</p><p><code>JSON.stringify()</code> を使用して、JSON オブジェクトを文字列である JSON テキストに変換し、ウェブサーバーに送信できるようにします。<br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/12/s_EAEEAE9063B0CA7CBC6574F36123E82B36B6C1EC3724A86DA7C0B4C67C2DD652_1645380076460_explaining-useeffect-local-storage.jpg" class="kg-image" alt="s_EAEEAE9063B0CA7CBC6574F36123E82B36B6C1EC3724A86DA7C0B4C67C2DD652_1645380076460_explaining-useeffect-local-storage" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/12/s_EAEEAE9063B0CA7CBC6574F36123E82B36B6C1EC3724A86DA7C0B4C67C2DD652_1645380076460_explaining-useeffect-local-storage.jpg 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2023/12/s_EAEEAE9063B0CA7CBC6574F36123E82B36B6C1EC3724A86DA7C0B4C67C2DD652_1645380076460_explaining-useeffect-local-storage.jpg 1000w, https://www.freecodecamp.org/japanese/news/content/images/size/w1600/2023/12/s_EAEEAE9063B0CA7CBC6574F36123E82B36B6C1EC3724A86DA7C0B4C67C2DD652_1645380076460_explaining-useeffect-local-storage.jpg 1600w, https://www.freecodecamp.org/japanese/news/content/images/size/w2400/2023/12/s_EAEEAE9063B0CA7CBC6574F36123E82B36B6C1EC3724A86DA7C0B4C67C2DD652_1645380076460_explaining-useeffect-local-storage.jpg 2400w" sizes="(min-width: 720px) 720px" width="2000" height="1095" loading="lazy"><figcaption>hooks が localStorage と連携してデータを格納する仕組み</figcaption></figure><h3 id="getitem-"><strong><code>getItem()</code> の使い方</strong></h3><p>このメソッドは localStorage からオブジェクトを取得します。React では他の方法もありますが、ここでは <code>useEffect()</code> を使用します。</p><p><code>useEffect()</code> は、最初のレンダリング時にすべてのアイテムを取得してくれます。つまり、コンポーネントがマウントまたは再レンダリングするたびに、localStorage からすべてのデータを取得します。</p><p><code>useEffect()</code> の第二引数を空にしているのはこのためです。</p><pre><code class="language-react">const [items, setItems] = useState([]);

useEffect(() =&gt; {
  const items = JSON.parse(localStorage.getItem('items'));
  if (items) {
   setItems(items);
  }
}, []);</code></pre><p>先ほどデータを localStorage に保存した時、データを JSON 文字列に変換したことを思い出してください。つまり、このデータを利用するためには、JSON 文字列を JSON オブジェクトに変換する必要があります。これは、<code>JSON.parse()</code> メソッドで行います。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/12/s_EAEEAE9063B0CA7CBC6574F36123E82B36B6C1EC3724A86DA7C0B4C67C2DD652_1645369611908_explaining-useeffect-local-storage2.jpg" class="kg-image" alt="s_EAEEAE9063B0CA7CBC6574F36123E82B36B6C1EC3724A86DA7C0B4C67C2DD652_1645369611908_explaining-useeffect-local-storage2" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2023/12/s_EAEEAE9063B0CA7CBC6574F36123E82B36B6C1EC3724A86DA7C0B4C67C2DD652_1645369611908_explaining-useeffect-local-storage2.jpg 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2023/12/s_EAEEAE9063B0CA7CBC6574F36123E82B36B6C1EC3724A86DA7C0B4C67C2DD652_1645369611908_explaining-useeffect-local-storage2.jpg 1000w, https://www.freecodecamp.org/japanese/news/content/images/size/w1600/2023/12/s_EAEEAE9063B0CA7CBC6574F36123E82B36B6C1EC3724A86DA7C0B4C67C2DD652_1645369611908_explaining-useeffect-local-storage2.jpg 1600w, https://www.freecodecamp.org/japanese/news/content/images/size/w2400/2023/12/s_EAEEAE9063B0CA7CBC6574F36123E82B36B6C1EC3724A86DA7C0B4C67C2DD652_1645369611908_explaining-useeffect-local-storage2.jpg 2400w" sizes="(min-width: 720px) 720px" width="2000" height="1095" loading="lazy"><figcaption>hooks が localstorage と連携してアイテムを取得する仕組み</figcaption></figure><h3 id="-"><strong>まとめ</strong></h3><p>この記事では、React hooks を使って localStorage を使う方法、使うタイミングやどの hooks を使うかについて学びました。</p><p>実際にこれがどのように機能するか知りたい方は、localStorage とこれらの Hooks を使用したシンプルな ToDo リストアプリの<a href="https://github.com/olawanlejoel/Todo-App">ソースコード</a>を参照してください。</p><p>state と props の詳細については、私が書いたこちらの<a href="https://joelolawanle.com/posts/understanding-state-props-react-key-differences-explained">記事</a>で学ぶことができます。また、こちらの <a href="https://joelolawanle.com/contents">content repository</a> から、私が書いた記事の全てを閲覧できます。</p><p></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[ 初心者向け 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[ React コンポーネント間でデータ・イベントを受け渡す方法 ]]>
                </title>
                <description>
                    <![CDATA[ API エンドポイントを利用する CRUD 操作を実装しようとすると、複数のコンポーネントにまたがるデータ管理の難しさに気付くでしょう。 あるいはモーダルを異なるコンポーネントからトリガーしたいこともあるでしょう。 そのような場合の対応方法は、なかなか理解が難しいかもしれません。 このチュートリアルでその方法を解説します。 親コンポーネントから子コンポーネントにデータを受け渡す方法 はじめに、親コンポーネントと子コンポーネントの間でデータを受け渡す場合を考えます。 まず、親と子にあたる 2 つのコンポーネントを作成します。 import React from 'react' export default function Parent() {   return (     <div>            </div>   ) } Parent.jsimport React from 'react' ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/pass-data-between-components-in-react/</link>
                <guid isPermaLink="false">643d4a4ce5e4fe05af495758</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ まつだようこ ]]>
                </dc:creator>
                <pubDate>Mon, 17 Apr 2023 23:30:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2023/04/Colorful-Animal-Crossing-Icons-Icon-Set-2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/pass-data-between-components-in-react/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Pass Data and Events Between Components in React</a>
      </p><p>API エンドポイントを利用する CRUD 操作を実装しようとすると、複数のコンポーネントにまたがるデータ管理の難しさに気付くでしょう。</p><p>あるいはモーダルを異なるコンポーネントからトリガーしたいこともあるでしょう。</p><p>そのような場合の対応方法は、なかなか理解が難しいかもしれません。</p><p>このチュートリアルでその方法を解説します。</p><h2 id="-">親コンポーネントから子コンポーネントにデータを受け渡す方法</h2><p>はじめに、親コンポーネントと子コンポーネントの間でデータを受け渡す場合を考えます。</p><p>まず、親と子にあたる 2 つのコンポーネントを作成します。</p><figure class="kg-card kg-code-card"><pre><code>import React from 'react'

export default function Parent() {
  return (
    &lt;div&gt;
      
    &lt;/div&gt;
  )
}
</code></pre><figcaption>Parent.js</figcaption></figure><figure class="kg-card kg-code-card"><pre><code>import React from 'react'

export default function Child() {
    return (
        &lt;div&gt;
            
        &lt;/div&gt;
    )
}
</code></pre><figcaption>Child.js</figcaption></figure><p>次に、Parent コンポーネントで Child コンポーネントをインポートして返します。</p><figure class="kg-card kg-code-card"><pre><code>import React from 'react'
import Child from './Child';

export default function Parent() {
  return (
    &lt;div&gt;
      &lt;Child/&gt;
    &lt;/div&gt;
  )
}
</code></pre><figcaption>Parent コンポーネント内で Child コンポーネントを呼び出す</figcaption></figure><p>そして関数と、その関数をトリガーするボタンを作成します。さらに <em>useState</em> フックを使用して、データを管理する state を作成します。</p><pre><code>import React from 'react'
import Child from './Child';
import { Button } from 'semantic-ui-react';
import { useState } from 'react';
import './App.css';

export default function Parent() {
  const [data, setData] = useState('');
  
  const parentToChild = () =&gt; {
    setData("This is data from Parent Component to the Child Component.");
  }
  return (
    &lt;div className="App"&gt;
      &lt;Child/&gt;
      
      &lt;div&gt;
        &lt;Button primary onClick={() =&gt; parentToChild()}&gt;Click Parent&lt;/Button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  )
}
</code></pre><p>上記のコードでは、<em>Click Parent</em> ボタンがクリックされた時に <em>parentToChild</em> 関数を呼び出します。Click Parent ボタンがクリックされると、<em>data</em> 変数に "This is data from Parent Component to the Child Component" が格納されます。</p><p>では次に、その data という state を Child コンポーネントに受け渡します。これは props を用いて実現できます。</p><p>下記のように、Child コンポーネントを呼び出す際に data を props として受け渡します。</p><figure class="kg-card kg-code-card"><pre><code>&lt;Child parentToChild={data}/&gt;
</code></pre><figcaption>Parent.js</figcaption></figure><p>ここでは <em>data</em> というデータを Child コンポーネントに受け渡しています。</p><p><code>data</code> が受け渡したいデータで、<code>parentToChild</code> が props の名前です。</p><p>次は、そのデータを Child コンポーネント側で受け取ります。これはとてもシンプルです。</p><p>これには 2 通りのケースがあります。</p><p>ケース 1: 関数コンポーネントを使用している場合は、引数で parentToChild を受け取ります。</p><figure class="kg-card kg-code-card"><pre><code>import React from 'react'

export default function Child({parentToChild}) {
    return (
        &lt;div&gt;
            {parentToChild}
        &lt;/div&gt;
    )
}
</code></pre><figcaption>React の関数コンポーネント</figcaption></figure><p>ケース 2: クラスコンポーネントの場合は、<code>this.props.parentToChild</code> を使用します。</p><figure class="kg-card kg-code-card"><pre><code>import React, { Component } from 'react'

export default class Child extends Component {
    render() {
        return (
            &lt;div&gt;
                {this.props.parentToChild}
            &lt;/div&gt;
        )
    }
}
</code></pre><figcaption>React のクラスコンポーネント</figcaption></figure><p>どちらのやり方でも同じ結果になります。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/04/Screenshot-2021-06-06-132836.png" class="kg-image" alt="Screenshot-2021-06-06-132836" width="600" height="400" loading="lazy"></figure><p><code>Click Parent</code> ボタンをクリックすると、データが画面に出力されます。</p><pre><code>import React from 'react'
import Child from './Child';
import { Button } from 'semantic-ui-react';
import { useState } from 'react';
import './App.css';

export default function Parent() {
  const [data, setData] = useState('');
  
  const parentToChild = () =&gt; {
    setData("This is data from Parent Component to the Child Component.");
  }
  return (
    &lt;div className="App"&gt;
      &lt;Child parentToChild={data}/&gt;
      
      &lt;div className="child"&gt;
        &lt;Button primary onClick={() =&gt; parentToChild()}&gt;Click Parent&lt;/Button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  )
}
</code></pre><p><code>Parent</code> コンポーネントの完成形のコードは上記の通りです。</p><h2 id="--1">子コンポーネントから親コンポーネントにデータを受け渡す方法</h2><p>こちらは少し厄介です。</p><p>まず、Parent コンポーネント内に <code>childToParent</code> という名前の関数と、<code>data</code> という名前の空の state を作成します。</p><figure class="kg-card kg-code-card"><pre><code>const [data, setData] = useState('');

const childToParent = () =&gt; {
   
}
</code></pre><figcaption>Parent コンポーネント</figcaption></figure><p>次に、<code>childToParent</code> 関数を props として Child コンポーネントに受け渡します。</p><figure class="kg-card kg-code-card"><pre><code>&lt;Child childToParent={childToParent}/&gt;
</code></pre><figcaption>Child コンポーネントに childToParent を受け渡す</figcaption></figure><p>そして Child コンポーネント内で、この関数を props として受け取り、onClick イベントに割り当てます。</p><p>さらに、文字列または数値形式のデータを持つ state (data) を宣言します。</p><p>その data を <code>parentToChild</code> 関数に引数として渡します。</p><figure class="kg-card kg-code-card"><pre><code>import React from 'react'
import { Button } from 'semantic-ui-react';

export default function Child({childToParent}) {
    const data = "This is data from Child Component to the Parent Component."
    return (
        &lt;div&gt;
            &lt;Button primary onClick={() =&gt; childToParent(data)}&gt;Click Child&lt;/Button&gt;
        &lt;/div&gt;
    )
}
</code></pre><figcaption>Child コンポーネント</figcaption></figure><p>次に、Parent コンポーネント側で、このデータを <code>childToParent</code> 関数内で引数として受け取ります。</p><p>そして useState フックを使用して data を設定します。</p><figure class="kg-card kg-code-card"><pre><code>import './App.css';
import { useState } from 'react';
import Child from './Child';

function Parent() {
  const [data, setData] = useState('');
  
  const childToParent = (childdata) =&gt; {
    setData(childdata);
  }

  return (
    &lt;div className="App"&gt;
      &lt;div&gt;
        &lt;Child/&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default Parent;
</code></pre><figcaption>Parent コンポーネント</figcaption></figure><p>次に、return 文の中でその data 変数を表示します。</p><figure class="kg-card kg-code-card"><pre><code>import './App.css';
import { useState } from 'react';
import Child from './Child';

function Parent() {
  const [data, setData] = useState('');
  
  const childToParent = (childdata) =&gt; {
    setData(childdata);
  }

  return (
    &lt;div className="App"&gt;
     {data}
      &lt;div&gt;
        &lt;Child childToParent={childToParent}/&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default Parent;
</code></pre><figcaption>Parent コンポーネント</figcaption></figure><p><code>Click Child</code> ボタンがクリックされると、子コンポーネントの data が親コンポーネントの data を上書きします。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/04/Screenshot-2021-06-06-134803.png" class="kg-image" alt="Screenshot-2021-06-06-134803" width="600" height="400" loading="lazy"></figure><p>これで、<strong>子コンポーネントから親コンポーネントへ</strong>、そして<strong>親コンポーネントから子コンポーネントへ</strong>データを受け渡す方法が分かりました。</p><h3 id="onclick-onchange-">onClick や OnChange のようなイベントを受け渡す</h3><p><code>childToParent</code> 関数の中で alert メソッドを呼び出すようにして、その関数を props として Child コンポーネントに受け渡します。</p><figure class="kg-card kg-code-card"><pre><code>import './App.css';
import Child from './Child';

function Parent() {
  const childToParent = () =&gt; {
    alert("This is an alert from the Child Component")
  }

  return (
    &lt;div className="App"&gt;
      &lt;div className="child"&gt;
        &lt;Child childToParent={childToParent}/&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default Parent;
</code></pre><figcaption>Parent コンポーネント</figcaption></figure><p>そして Child コンポーネントで <code>childToParent</code> を props として受け取り、ボタンの onClick イベントに割り当てます。</p><figure class="kg-card kg-code-card"><pre><code>import React from 'react'
import { Button } from 'semantic-ui-react';

export default function Child({childToParent}) {
    return (
        &lt;div&gt;
            &lt;Button primary onClick={() =&gt; childToParent()}&gt;Click Child&lt;/Button&gt;
        &lt;/div&gt;
    )
}
</code></pre><figcaption>Child コンポーネント</figcaption></figure><p>Child コンポーネント内のボタンをクリックすると、Parent コンポーネント内の関数が呼び出され、次のようなアラートが表示されます。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2023/04/Screenshot-2021-06-06-140405.png" class="kg-image" alt="Screenshot-2021-06-06-140405" width="600" height="400" loading="lazy"></figure><p>以上です！</p><p>もっといろいろ実験したければ、<a href="https://github.com/nishant-666/Passing-data-in-React">GitHub にコードを公開しています</a>のでご利用ください。</p><p>この記事は以上です。学習を楽しんでください。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React の背景画像設定チュートリアル – インライン CSS で backgroundImage を設定する方法 ]]>
                </title>
                <description>
                    <![CDATA[ React のインライン CSS を使って backgroundImage スタイルプロパティを設定する方法は、4 つあります。 このチュートリアルでは、サンプルコードとともに、4 つ全ての方法をご紹介します。 外部 URL を使って React の背景画像を設定する方法 設定したい背景画像がオンライン上にあるならば、下のコードのように URL を記述すると背景画像を設定できます。 function App() {   return (     <div style={{        backgroundImage: `url("https://via.placeholder.com/500")`      }}>      ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/react-background-image-tutorial-how-to-set-backgroundimage-with-inline-css-style/</link>
                <guid isPermaLink="false">638c987c3a21f205f6eea5d1</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ YUUSUKE OKAMOTO ]]>
                </dc:creator>
                <pubDate>Mon, 05 Dec 2022 23:30:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2022/12/fcc-bg-image-2-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/react-background-image-tutorial-how-to-set-backgroundimage-with-inline-css-style/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">React Background Image Tutorial – How to Set backgroundImage with Inline CSS Style</a>
      </p><p>React のインライン CSS を使って backgroundImage スタイルプロパティを設定する方法は、4 つあります。</p><p>このチュートリアルでは、サンプルコードとともに、4 つ全ての方法をご紹介します。</p><h2 id="-url-react-">外部 URL を使って React の背景画像を設定する方法</h2><p>設定したい背景画像がオンライン上にあるならば、下のコードのように URL を記述すると背景画像を設定できます。</p><figure class="kg-card kg-code-card"><pre><code class="language-jsx">function App() {
  return (
    &lt;div style={{ 
      backgroundImage: `url("https://via.placeholder.com/500")` 
    }}&gt;
      Hello World
    &lt;/div&gt;
  );
}</code></pre><figcaption>外部 URL で背景画像を設定</figcaption></figure><p>上のコードは、<code>background-image: url(https://via.placeholder.com/500)</code> のスタイルが適用された <code>&lt;div&gt;</code> 要素を描画します。</p><h2 id="-src-react-">アプリの /src フォルダ配下から React の背景画像を設定する方法</h2><p>Create React App コマンドを使ってアプリケーションを開発し、<code>src/</code> フォルダ内に画像が配置されているならば、<code>import</code> で画像を取り込んで、要素の背景として設定できます。</p><figure class="kg-card kg-code-card"><pre><code class="language-jsx">import React from "react";
import background from "./img/placeholder.png";

function App() {
  return (
    &lt;div style={{ backgroundImage: `url(${background})` }}&gt;
      Hello World
    &lt;/div&gt;
  );
}

export default App;</code></pre><figcaption>取り込んだ画像を用いて背景画像を設定</figcaption></figure><p><code>npm start</code> コマンドにてアプリケーションを起動する際に、画像が見つからない場合には、React アプリケーションはコンパイルに失敗したというエラーを表示してビルドを停止します。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/12/React-failed-to-compile-image-1.png" class="kg-image" alt="React-failed-to-compile-image-1" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/12/React-failed-to-compile-image-1.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/12/React-failed-to-compile-image-1.png 717w" width="717" height="240" loading="lazy"><figcaption>画像を見つけられないという React のコンパイルエラー</figcaption></figure><p>この方法を使えば、アプリケーション上で壊れた画像リンクが表示されることはありません。上のコードにおいて <code>backgroundImage</code> は、JavaScript の表記を埋め込むためのテンプレート文字列を使っています。</p><h2 id="-url-react--1">相対 URL を使って React の背景画像を設定する方法</h2><p></p><p>Create React App コマンドで作成された React アプリケーションの public フォルダは、静的な画像などを追加するのに使われます。<code>public/</code> フォルダ内に配置したどのようなファイルも、オンラインでアクセスできます。</p><p>public フォルダ内部に <code>image.png</code> ファイルを配置している場合、<code>&lt;ホストのアドレス&gt;/image.png</code> の URL にて画像情報にアクセスできます。ローカル環境下で React アプリを動かしているとき、画像は <code>http://localhost:3000/image.png</code> でアクセスできます。</p><p>そしてホストアドレスに対する相対パスの URL を与えることで、背景画像を設定できます。以下のコードは一例です。</p><figure class="kg-card kg-code-card"><pre><code class="language-jsx">&lt;div style={{ backgroundImage: "url(/image.png)" }}&gt;
  Hello World
&lt;/div&gt;</code></pre><figcaption>相対パスで背景画像を設定する方法</figcaption></figure><p>上記のサンプルコードのように、URL のパスを <code>/image.png</code> に設定することで、ブラウザは <code>&lt;ホストのアドレス&gt;/image.png</code> の場所で背景画像を探すことになります。</p><p>フォルダを使って画像を整理したいならば、<code>public/</code> フォルダ内に別の画像用のフォルダを作成することもできます。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/12/Screen-Shot-2020-12-14-at-20.18.30.png" class="kg-image" alt="Screen-Shot-2020-12-14-at-20.18.30" width="360" height="137" loading="lazy"><figcaption>public フォルダ内に img フォルダを作成する</figcaption></figure><p>フォルダを新しく作成するなら、背景画像の値も <code>url(/img/image.png)</code> に変更するのを忘れないようにしましょう。</p><h2 id="-react-">絶対パスを使って React の背景画像を設定する方法</h2><p>以下のコードのように Create React App で作成したアプリの <code>PUBLIC_URL</code> の環境変数を使うことで、絶対パス URL を含めることができます。</p><figure class="kg-card kg-code-card"><pre><code class="language-jsx">&lt;div style={{ 
  backgroundImage: `url(${process.env.PUBLIC_URL + '/image.png'})` 
}}&gt;
  Hello World
&lt;/div&gt;</code></pre><figcaption>絶対パス URL で背景画像を設定する方法</figcaption></figure><p>ローカル環境上で上記のコードを動かすとき、React スクリプトは、<code>PUBLIC_URL</code> の値をコントロールします。ローカル環境で動かすときは、絶対パスの URL の代わりに、相対パス URL のように見えます。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/12/absolute-url-background-image-1.png" class="kg-image" alt="absolute-url-background-image-1" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/12/absolute-url-background-image-1.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/12/absolute-url-background-image-1.png 648w" width="648" height="68" loading="lazy"><figcaption>画像の絶対パス URL は、ローカルコンピュータで表示されません。</figcaption></figure><p>React を本番アプリケーションにデプロイしたときだけ、絶対パス URL が表示されます。</p><h2 id="-">追加のプロパティと一緒に背景画像を設定する方法</h2><p>もし、さらに背景画像をカスタマイズしたいならば、<code>backgroundImage</code> の後に追加のプロパティを追加することで、カスタマイズできます。以下のコードはサンプルです。</p><figure class="kg-card kg-code-card"><pre><code class="language-jsx">
&lt;div style={{ 
  backgroundImage: `url(${process.env.PUBLIC_URL + '/image.png'})`,
  backgroundRepeat: 'no-repeat',
  width:'250px' 
}}&gt;
  Hello World
&lt;/div&gt;</code></pre><figcaption>追加プロパティを背景画像に設定</figcaption></figure><p>上記のコードのプロパティには、<code>&lt;div&gt;</code> 要素に <code>background-image</code> を設定すると同時に、<code>background-repeat: no-repeat</code> と <code>width: 250px</code> の CSS を追加しています。</p><p>ここまでお読みいただきありがとうございます。そして、この記事が読者の皆様にとって、役に立っていることを願っています。質問があれば、<a href="https://twitter.com/nsebhastian">筆者の Twitter</a> までお知らせください。今後も、開発者向けに役立つちょっとした情報を随時紹介していく予定です。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React、React Hooks、Axios を用いて CRUD 操作を実行する方法 ]]>
                </title>
                <description>
                    <![CDATA[ React を扱っていれば、API リクエストを理解し実装するのはとても難しいと感じられるでしょう。 そこでこの記事では、React 、React Hooks、React Router、Axios を用いて CRUD 操作を実装することにより、そのすべてがどのように機能するかを学んでいきます。 さあはじめましょう。 Node と npm をインストールする方法 まず最初に、Node をシステム内にインストールしましょう。私たちは主にそれを用いて JavaScript のコードを実行します。 Node をダウンロードするには、https://nodejs.org/en/ にアクセスしてください。 Node上に構築された Node パッケージマネージャー (npm) も必要です。それを用いることで、JavaScript アプリ用のパッケージをインストールできます。幸い、npm は Node に備わっているため、個別にダウンロードする必要はありません。 両方のダウンロードが終了したら、ターミナルかコマンドプロンプトを開いて、node -v と入力してみましょう。これでダウンロード ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/how-to-perform-crud-operations-using-react/</link>
                <guid isPermaLink="false">61f13a510e2ac404be7310fd</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AXIOS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manabu Matsumoto ]]>
                </dc:creator>
                <pubDate>Sun, 06 Mar 2022 23:45:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2022/01/React-CRUD-Operations-using-React-and-React-Hooks.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/how-to-perform-crud-operations-using-react/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Perform CRUD Operations using React, React Hooks, and Axios</a>
      </p><p>React を扱っていれば、API リクエストを理解し実装するのはとても難しいと感じられるでしょう。</p><p>そこでこの記事では、React 、React Hooks、React Router、Axios を用いて CRUD 操作を実装することにより、そのすべてがどのように機能するかを学んでいきます。</p><p>さあはじめましょう。</p><h2 id="node-npm-"><strong>Node と npm をインストールする方法</strong></h2><p>まず最初に、Node をシステム内にインストールしましょう。私たちは主にそれを用いて JavaScript のコードを実行します。</p><p>Node をダウンロードするには、<a href="https://nodejs.org/en/">https://nodejs.org/en/</a> にアクセスしてください。</p><p>Node上に構築された <strong>Node パッケージマネージャー</strong> (npm) も必要です。それを用いることで、JavaScript アプリ用のパッケージをインストールできます。幸い、npm は Node に備わっているため、個別にダウンロードする必要はありません。</p><p>両方のダウンロードが終了したら、ターミナルかコマンドプロンプトを開いて、<code>node -v</code> と入力してみましょう。これでダウンロードした Node のバージョンを確認します。</p><h2 id="react-"><strong>React アプリケーションを作成する方法</strong></h2><p>次に、React アプリケーションを作成するには、ターミナル内で <code><strong><strong>npx create-react-app &lt;Your app name&gt;</strong></strong></code> と入力しますが、今回は <code>npx create-react-app react-crud</code> と入力します。</p><p>そうすると、パッケージのインストール状態が表示されます。</p><p>パッケージのインストールが終わったら、プロジェクトフォルダーへ移動して、<code>npm start</code> と入力します。</p><p>すると、次のような初期状態の React テンプレートが表示されます:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/Screenshot-2021-07-24-124754.png" class="kg-image" alt="Screenshot-2021-07-24-124754" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/Screenshot-2021-07-24-124754.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/01/Screenshot-2021-07-24-124754.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/size/w1600/2022/01/Screenshot-2021-07-24-124754.png 1600w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/Screenshot-2021-07-24-124754.png 1846w" sizes="(min-width: 720px) 720px" width="1846" height="903" loading="lazy"><figcaption>初期状態の React テンプレート</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/Screenshot-2021-07-24-124858.png" class="kg-image" alt="Screenshot-2021-07-24-124858" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/Screenshot-2021-07-24-124858.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/01/Screenshot-2021-07-24-124858.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/Screenshot-2021-07-24-124858.png 1117w" sizes="(min-width: 720px) 720px" width="1117" height="670" loading="lazy"><figcaption>App.js ファイル</figcaption></figure><h2 id="react-semantic-ui-"><strong>React 用の Semantic UI パッケージをインストールする方法</strong></h2><p>次に、Semantic UI React パッケージをインストールしましょう。Semantic UI は、React 用に作られた UI (ユーザーインターフェース) ライブラリーです。Semantic UI には、テーブル、ボタンなどの機能をもつ多くの UI コンポーネントが、あらかじめ構築されています。</p><p>以下のコマンドを、ご自身のパッケージマネージャーに応じて入力すれば、Semantic UI をインストールできます。</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">yarn add semantic-ui-react semantic-ui-css
</code></pre><figcaption>Yarn パッケージマネージャー用のコマンド</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-bash">npm install semantic-ui-react semantic-ui-css
</code></pre><figcaption>NPM パッケージマネージャー用のコマンド</figcaption></figure><p>また、アプリケーションのメインエントリーファイルである index.js に、インストールしたライブラリーをインポートします。</p><figure class="kg-card kg-code-card"><pre><code class="language-js">import 'semantic-ui-css/semantic.min.css'
</code></pre><figcaption>こちらを index.js ファイル内へ貼り付けてください</figcaption></figure><h2 id="crud-"><strong>CRUD アプリケーションを構築する方法</strong></h2><p>では、React を用いて CRUD アプリケーションの構築を始めましょう。</p><p>最初に、アプリケーションにヘッダーを追加しましょう。</p><p>以下のようにして、App.js ファイル内にヘッダーを追加します:</p><figure class="kg-card kg-code-card"><pre><code>import './App.css';

function App() {
  return (
    &lt;div&gt;
      React Crud Operations
    &lt;/div&gt;
  );
}

export default App;
</code></pre><figcaption>アプリケーションにヘッダーを追加</figcaption></figure><p>次に、ヘッダーを中央に配置しましょう。</p><p>親要素 div の、className 属性の値を main に設定してください。それから、App.css ファイル内でフレックスボックスを使用して、ヘッダーを中央に配置しましょう。</p><figure class="kg-card kg-code-card"><pre><code>import './App.css';

function App() {
  return (
    &lt;div className="main"&gt;
      React Crud Operations
    &lt;/div&gt;
  );
}

export default App;
</code></pre><figcaption>App.js の中で 親要素 div の className 属性の値を main に設定</figcaption></figure><figure class="kg-card kg-code-card"><pre><code>.main{
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #212121;
  color: whitesmoke;
}
</code></pre><figcaption>App.css ファイル</figcaption></figure><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/Screenshot-2021-07-24-130340.png" class="kg-image" alt="Screenshot-2021-07-24-130340" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/Screenshot-2021-07-24-130340.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/01/Screenshot-2021-07-24-130340.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/Screenshot-2021-07-24-130340.png 1234w" sizes="(min-width: 720px) 720px" width="1234" height="567" loading="lazy"></figure><p>これでヘッダーは完全に中央に配置されました。</p><p>見た目が少し良くなるように、ヘッダーをより目立たせて、かっこいいフォントを設定する必要があります。そのために、まずは以下のようにして見出しタグをヘッダーの周りに追加します:</p><pre><code>import './App.css';

function App() {
  return (
    &lt;div className="main"&gt;
      &lt;h2&gt;React Crud Operations&lt;/h2&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre><p>次に Google Fonts からフォントをインポートしましょう。 <a href="https://fonts.google.com/">https://fonts.google.com/</a> にアクセスし、1 つ選択します。</p><p>お好みのフォントを選択してください。ただし、この記事の中では Montserrat フォントファミリーを使用します。</p><p>フォントを選択したら、次のようにして App.css ファイル内でフォントをインポートしてください:</p><pre><code>@import url('https://fonts.googleapis.com/css2?family=Montserrat&amp;display=swap');
</code></pre><p>では、ヘッダーのフォントを変更しましょう。</p><pre><code>    &lt;div className="main"&gt;
      &lt;h2 className="main-header"&gt;React Crud Operations&lt;/h2&gt;
    &lt;/div&gt;
</code></pre><p>このように、見出しタグの <code>className</code> 属性の値を、<code>main-header</code> に設定してください。</p><p>それから、App.css 内で、Montserrat フォントファミリーを追加してください:</p><pre><code>.main-header{
  font-family: 'Montserrat', sans-serif;
}
</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/Screenshot-2021-07-24-132749-1.jpg" class="kg-image" alt="Screenshot-2021-07-24-132749-1" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/Screenshot-2021-07-24-132749-1.jpg 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/Screenshot-2021-07-24-132749-1.jpg 822w" sizes="(min-width: 720px) 720px" width="822" height="368" loading="lazy"></figure><p>これでヘッダーが変更されたのが確認できます。</p><h2 id="crud--1">CRUD コンポーネントを作成する方法</h2><p>これから、4 つの CRUD 操作 (Create - 生成、Read - 読み取り、Update - 更新、Delete - 削除) に対応するコンポーネントを作成してみましょう。</p><p>src フォルダーの中に、components というフォルダーを作成してください。それから、このフォルダーの中に create、read、update という 3 つのファイルを作成してください。delete については、コンポーネントを追加する必要はありません。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/Screenshot-2021-07-24-133242.png" class="kg-image" alt="Screenshot-2021-07-24-133242" width="334" height="107" loading="lazy"></figure><p>では、Create (生成) 操作を実装しましょう。</p><p>ただそのためには、Mock API を使用する必要があります。この API は作成したデータを偽のサーバーへ送信するもので、単に学習目的で使用します。</p><p>それでは、<a href="https://mockapi.io/">https://mockapi.io/</a> へアクセスし、アカウントを作成してください。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/mockAPI.jpg" class="kg-image" alt="mockAPI" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/mockAPI.jpg 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/01/mockAPI.jpg 1000w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/mockAPI.jpg 1545w" sizes="(min-width: 720px) 720px" width="1545" height="865" loading="lazy"><figcaption>MockAPI</figcaption></figure><p>プラスボタンをクリックしてプロジェクトを作成してください。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/createProject.jpg" class="kg-image" alt="createProject" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/createProject.jpg 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/createProject.jpg 999w" sizes="(min-width: 720px) 720px" width="999" height="329" loading="lazy"><figcaption>プラスボタンをクリックして新しいプロジェクトを作成</figcaption></figure><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/projectName.png" class="kg-image" alt="projectName" width="508" height="576" loading="lazy"></figure><p>プロジェクトの名前を入力して、CREATE ボタンをクリックしてください。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/newResorce.png" class="kg-image" alt="newResorce" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/newResorce.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/01/newResorce.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/newResorce.png 1039w" sizes="(min-width: 720px) 720px" width="1039" height="370" loading="lazy"></figure><p>NEW RESOURCE ボタンをクリックして新しいリソースを作成してください。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/resorceName.png" class="kg-image" alt="resorceName" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/resorceName.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/resorceName.png 741w" sizes="(min-width: 720px) 720px" width="741" height="892" loading="lazy"></figure><p>Resorce Name の欄へ名前を入力することを求められますので、使用する名前を入力してください。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/resorceNameFakeData-1.png" class="kg-image" alt="resorceNameFakeData-1" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/resorceNameFakeData-1.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/resorceNameFakeData-1.png 734w" sizes="(min-width: 720px) 720px" width="734" height="902" loading="lazy"></figure><p>name、avatar、createdAt などの余分な欄は必要ありませんので削除してください。その後で、CREATE をクリックしてください。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/createFakeAPI.png" class="kg-image" alt="createFakeAPI" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/createFakeAPI.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/01/createFakeAPI.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/createFakeAPI.png 1023w" sizes="(min-width: 720px) 720px" width="1023" height="333" loading="lazy"></figure><p>これで、fakeData という名前の、偽の API を作成できました。</p><p>fakeData の上でクリックすると、新しいタブで API が開くのを確認できます。現在データベースは空です。</p><h2 id="create-">Create コンポーネント用のフォームを作成する方法</h2><p>次に、Semantic UI ライブラリーが提供しているフォームを使用してみましょう。</p><p>Semantic UI React へアクセスし、左側の検索バーの中で Form を検索してください。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/SemanticReactForm.png" class="kg-image" alt="SemanticReactForm" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/SemanticReactForm.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/01/SemanticReactForm.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/SemanticReactForm.png 1219w" sizes="(min-width: 720px) 720px" width="1219" height="472" loading="lazy"></figure><p>このようなフォームが表示されるので、上部左側の Try it をクリックして、コードを取得してください。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/FormCode.png" class="kg-image" alt="FormCode" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/FormCode.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/01/FormCode.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/FormCode.png 1194w" sizes="(min-width: 720px) 720px" width="1194" height="732" loading="lazy"></figure><p>コードをコピーしたら、次のように create.js ファイルの中に貼り付けてください:</p><pre><code>import React from 'react'
import { Button, Checkbox, Form } from 'semantic-ui-react'

const Create = () =&gt; (
    &lt;Form&gt;
        &lt;Form.Field&gt;
            &lt;label&gt;First Name&lt;/label&gt;
            &lt;input placeholder='First Name' /&gt;
        &lt;/Form.Field&gt;
        &lt;Form.Field&gt;
            &lt;label&gt;Last Name&lt;/label&gt;
            &lt;input placeholder='Last Name' /&gt;
        &lt;/Form.Field&gt;
        &lt;Form.Field&gt;
            &lt;Checkbox label='I agree to the Terms and Conditions' /&gt;
        &lt;/Form.Field&gt;
        &lt;Button type='submit'&gt;Submit&lt;/Button&gt;
    &lt;/Form&gt;
)

export default Create;
</code></pre><p>では、Create コンポーネントを App.js ファイルの中でインポートしてください。</p><pre><code>import './App.css';
import Create from './components/create';

function App() {
  return (
    &lt;div className="main"&gt;
      &lt;h2 className="main-header"&gt;React Crud Operations&lt;/h2&gt;
      &lt;div&gt;
        &lt;Create/&gt;
      &lt;/div&gt;
    &lt;/div&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/2022/01/formInit.png" class="kg-image" alt="formInit" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/formInit.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/formInit.png 882w" sizes="(min-width: 720px) 720px" width="882" height="579" loading="lazy"></figure><p>しかし、ここで問題が発生します – 項目が適切にそろえられていませんし、テキスト入力欄のラベルの色が黒になってしまっています。では、これを修正してみましょう。</p><p>create.js ファイルの中で、<strong>Form</strong> の className の値を <code>create-form</code> に設定してください。</p><figure class="kg-card kg-code-card"><pre><code>import React from 'react'
import { Button, Checkbox, Form } from 'semantic-ui-react'

const Create = () =&gt; (
    &lt;Form className="create-form"&gt;
        &lt;Form.Field&gt;
            &lt;label&gt;First Name&lt;/label&gt;
            &lt;input placeholder='First Name' /&gt;
        &lt;/Form.Field&gt;
        &lt;Form.Field&gt;
            &lt;label&gt;Last Name&lt;/label&gt;
            &lt;input placeholder='Last Name' /&gt;
        &lt;/Form.Field&gt;
        &lt;Form.Field&gt;
            &lt;Checkbox label='I agree to the Terms and Conditions' /&gt;
        &lt;/Form.Field&gt;
        &lt;Button type='submit'&gt;Submit&lt;/Button&gt;
    &lt;/Form&gt;
)

export default Create;
</code></pre><figcaption>create.js</figcaption></figure><p>そして、 App.css ファイルの中に以下のクラスセレクターを追加してください:</p><figure class="kg-card kg-code-card"><pre><code>.create-form label{
  color: whitesmoke !important;
  font-family: 'Montserrat', sans-serif;
  font-size: 12px !important;
}
</code></pre><figcaption>App.css</figcaption></figure><p>このクラスセレクターはフォームの各欄のラベルを指定して、文字色を whitesmoke に設定しています。また、フォントを変更して、フォントサイズを大きくしています。</p><p>では、main という <code>className</code> のセレクターの中で、flex-direction プロパティーを追加してください。このプロパティーの値を column 方向に設定することで、<code>className</code> が main である要素の中の各要素が、垂直にそろえられます。</p><figure class="kg-card kg-code-card"><pre><code>.main{
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #212121;
  color: whitesmoke;
  flex-direction: column;
}
</code></pre><figcaption>App.css</figcaption></figure><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/columnDirectionForm.png" class="kg-image" alt="columnDirectionForm" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/columnDirectionForm.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/columnDirectionForm.png 757w" sizes="(min-width: 720px) 720px" width="757" height="623" loading="lazy"></figure><p>これで、フォームの外観が改善されるのが確認できます。</p><p>では次に、フォーム欄からデータを取得してコンソールの中で表示してみましょう。そのために、React の <code>useState</code> フックを使用します。</p><p>create.js ファイルの中で、React から <code>useState</code> をインポートしてください。</p><pre><code>import React, { useState } from 'react';
</code></pre><p>その後で、First Name、Last Name、チェックボックス用のステート (state) を作成してください。各ステートは、空のストリングまたは false で初期化します。</p><pre><code>import React, { useState } from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react'

export default function Create() {
    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    const [checkbox, setCheckbox] = useState(false);
    return (
        &lt;div&gt;
            &lt;Form className="create-form"&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;First Name&lt;/label&gt;
                    &lt;input placeholder='First Name' /&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;Last Name&lt;/label&gt;
                    &lt;input placeholder='Last Name' /&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;Checkbox label='I agree to the Terms and Conditions' /&gt;
                &lt;/Form.Field&gt;
                &lt;Button type='submit'&gt;Submit&lt;/Button&gt;
            &lt;/Form&gt;
        &lt;/div&gt;
    )
}
</code></pre><p>現在、Create コンポーネントは関数コンポーネントとして機能しているのが確認できます。フックは、関数コンポーネントの中だけで使用できるので、コンポーネントを関数コンポーネントに変更する必要があります。</p><p>では、First Name、Last Name、チェックボックスの各欄の値を、<code>setFirstName</code>、<code>setLastName</code>、<code>setCheckbox</code> プロパティーを用いて、それぞれ設定しましょう。</p><pre><code>&lt;input placeholder='First Name' onChange={(e) =&gt; setFirstName(e.target.value)}/&gt;

&lt;input placeholder='Last Name' onChange={(e) =&gt; setLastName(e.target.value)}/&gt;

&lt;Checkbox label='I agree to the Terms and Conditions' onChange={(e) =&gt; setCheckbox(!checkbox)}/&gt;
</code></pre><p>それから、firstName、lastName、checkbox の各ステートの値を取得してみましょう。</p><p>ここで、後から API へデータを送信するのに使用する、<code>postData</code> という名の関数を作成します。その関数の中に、次のコードを記述してください:</p><pre><code>const postData = () =&gt; {
        console.log(firstName);
        console.log(lastName);
        console.log(checkbox);
}
</code></pre><p>では、First Name、Last Name、チェックボックスの値をコンソールに出力してみましょう。</p><p>Submit ボタンに、onClick イベントを使用してこの関数を割り当てることによって、Submit ボタンを押したときに、この関数が呼び出されるようにします。</p><pre><code>&lt;Button onClick={postData} type='submit'&gt;Submit&lt;/Button&gt;
</code></pre><p>これまでの内容をすべて含んだ、<em>create</em> ファイル用のコードはこちらです:</p><pre><code>import React, { useState } from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react'

export default function Create() {
    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    const [checkbox, setCheckbox] = useState(false);
    const postData = () =&gt; {
        console.log(firstName);
        console.log(lastName);
        console.log(checkbox);
    }
    return (
        &lt;div&gt;
            &lt;Form className="create-form"&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;First Name&lt;/label&gt;
                    &lt;input placeholder='First Name' onChange={(e) =&gt; setFirstName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;Last Name&lt;/label&gt;
                    &lt;input placeholder='Last Name' onChange={(e) =&gt; setLastName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;Checkbox label='I agree to the Terms and Conditions' onChange={(e) =&gt; setCheckbox(!checkbox)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Button onClick={postData} type='submit'&gt;Submit&lt;/Button&gt;
            &lt;/Form&gt;
        &lt;/div&gt;
    )
}
</code></pre><p>First name と Last name の中に値を入力して、チェックボックスにチェックを入れてください。その後で、Submit ボタンをクリックしてみてください。すると次のように、コンソールの中にデータがポップアップするのが確認できます:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/dataPopUp.png" class="kg-image" alt="dataPopUp" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/dataPopUp.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/01/dataPopUp.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/dataPopUp.png 1270w" sizes="(min-width: 720px) 720px" width="1270" height="880" loading="lazy"></figure><h2 id="axios-api-mock-api-">Axios を使って API リクエストを Mock API へ送信する方法</h2><p>今度は、Axios を使ってフォームのデータを偽のサーバーへ送信してみましょう。</p><p>しかしまずは、Axios をインストールする必要があります。</p><p><code>npm i axios</code> と入力して、パッケージをインストールしてください。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/AxiosInstall.png" class="kg-image" alt="AxiosInstall" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/AxiosInstall.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/AxiosInstall.png 896w" sizes="(min-width: 720px) 720px" width="896" height="416" loading="lazy"></figure><p>パッケージのインストールが終了したら、Create 操作の実装を開始しましょう。</p><p>Axios をファイルの先頭でインポートしてください。</p><figure class="kg-card kg-code-card"><pre><code>import axios from 'axios';
</code></pre><figcaption>Axios のインポート</figcaption></figure><p><code>postData</code> 関数の中で、Axios を使用して POST リクエストを送信します。</p><figure class="kg-card kg-code-card"><pre><code>const postData = () =&gt; {
        axios.post(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData`, {
            firstName,
            lastName,
            checkbox
        })
}
</code></pre><figcaption>POST リクエストの送信</figcaption></figure><p>ご覧のとおり、ここでは axios.post を使用しています。また axios.post の中に、先ほど作成した API のエンドポイントがあります。その後に、中括弧で囲まれたフォームの項目があります。</p><p>Submit をクリックした時に、この関数が呼び出され API サーバーにデータを送信します。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/postFormData.png" class="kg-image" alt="postFormData" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/postFormData.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/postFormData.png 615w" width="615" height="432" loading="lazy"></figure><p>では、First Name と Last Name を入力して、チェックボックスにチェックを入れたら、Submit をクリックしてください。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/postedFormData.png" class="kg-image" alt="postedFormData" width="512" height="32" loading="lazy"></figure><p>その後で API を確認してみると、オブジェクトの中に囲まれた状態で、入力した First Name と Last Nameの値と、true という値に設定された checkbox を取得できます。</p><h2 id="read-update-delete-">Read 、Update、Delete の各操作を実装する方法</h2><p>Read (読み取り) 操作の実装を開始するために、Read ページを作成する必要があります。また、React Router パッケージを用いて、ユーザーをさまざまなページへ案内する必要があります。</p><p><a href="https://reactrouter.com/web/guides/quick-start">https://reactrouter.com/web/guides/quick-start</a> へアクセスし、また、<code>npm i react-router-dom@5</code> を使用してパッケージをインストールしてください。</p><p>パッケージのインストールが済んだら、React Router からコンポーネントをいくつかインポートします:</p><figure class="kg-card kg-code-card"><pre><code>import { BrowserRouter as Router, Route } from 'react-router-dom'
</code></pre><figcaption>React Router から Router と Route をインポート</figcaption></figure><p>App.js の中で、return の中のコードをすべて Router でラップしてください。基本的に Router の内部にあるものは、 React の中でルーティングを使用できることを意味しています。</p><pre><code>import './App.css';
import Create from './components/create';
import { BrowserRouter as Router, Route } from 'react-router-dom'

function App() {
  return (
    &lt;Router&gt;
      &lt;div className="main"&gt;
        &lt;h2 className="main-header"&gt;React Crud Operations&lt;/h2&gt;
        &lt;div&gt;
          &lt;Create /&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;
</code></pre><p>現在、App.js は上のような状態でしょう。</p><p>では、return 内の Create を置き換えて、以下のコードを追加してください:</p><pre><code>import './App.css';
import Create from './components/create';
import { BrowserRouter as Router, Route } from 'react-router-dom'

function App() {
  return (
    &lt;Router&gt;
      &lt;div className="main"&gt;
        &lt;h2 className="main-header"&gt;React Crud Operations&lt;/h2&gt;
        &lt;div&gt;
          &lt;Route exact path='/create' component={Create} /&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;
</code></pre><p>ここでは、Create をルートコンポーネントとして使用しています。Create のパスは '/create' に設定しました。そのため、<a href="http://localhost:3000/create">http://localhost:3000/create</a> へアクセスすれば、Create ページを見ることができます。</p><p>同じように、read と update に対応するルートが必要です。</p><pre><code>import './App.css';
import Create from './components/create';
import Read from './components/read';
import Update from './components/update';
import { BrowserRouter as Router, Route } from 'react-router-dom'

function App() {
  return (
    &lt;Router&gt;
      &lt;div className="main"&gt;
        &lt;h2 className="main-header"&gt;React Crud Operations&lt;/h2&gt;
        &lt;div&gt;
          &lt;Route exact path='/create' component={Create} /&gt;
        &lt;/div&gt;
        &lt;div style={{ marginTop: 20 }}&gt;
          &lt;Route exact path='/read' component={Read} /&gt;
        &lt;/div&gt;

        &lt;Route path='/update' component={Update} /&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;
</code></pre><p>上のコードで確認できるように、read と update のルートを作成します。</p><p>ここで <a href="http://localhost:3000/read">http://localhost:3000/read</a> へアクセスすると、Read ページが次のように表示されます:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/02/planeReadComponent.png" class="kg-image" alt="planeReadComponent" width="513" height="242" loading="lazy"><figcaption>Read ルート</figcaption></figure><p>また、<a href="http://localhost:3000/update">http://localhost:3000/update</a> では、次のように Update コンポーネントが表示されます:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/02/planeUpdateComponent.png" class="kg-image" alt="planeUpdateComponent" width="505" height="281" loading="lazy"></figure><h3 id="read-">Read 操作</h3><p>Read 操作には、テーブルコンポーネントが必要です。そのため、React Semantic UI にアクセスし、ライブラリーが提供しているテーブルを、以下のように使用してください。</p><figure class="kg-card kg-code-card"><pre><code>import React from 'react';
import { Table } from 'semantic-ui-react'
export default function Read() {
    return (
        &lt;div&gt;
            &lt;Table singleLine&gt;
                &lt;Table.Header&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.HeaderCell&gt;Name&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;Registration Date&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;E-mail address&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;Premium Plan&lt;/Table.HeaderCell&gt;
                    &lt;/Table.Row&gt;
                &lt;/Table.Header&gt;

                &lt;Table.Body&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.Cell&gt;John Lilki&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;September 14, 2013&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;jhlilk22@yahoo.com&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;No&lt;/Table.Cell&gt;
                    &lt;/Table.Row&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.Cell&gt;Jamie Harington&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;January 11, 2014&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;jamieharingonton@yahoo.com&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;Yes&lt;/Table.Cell&gt;
                    &lt;/Table.Row&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.Cell&gt;Jill Lewis&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;May 11, 2014&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;jilsewris22@yahoo.com&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;Yes&lt;/Table.Cell&gt;
                    &lt;/Table.Row&gt;
                &lt;/Table.Body&gt;
            &lt;/Table&gt;
        &lt;/div&gt;
    )
}
</code></pre><figcaption>read.js</figcaption></figure><p>ここではテーブルの中にいくつかのダミーデータが用いられていますが、1 列分のデータだけが必要なので、残りは削除しましょう。</p><figure class="kg-card kg-code-card"><pre><code>import React from 'react';
import { Table } from 'semantic-ui-react'
export default function Read() {
    return (
        &lt;div&gt;
            &lt;Table singleLine&gt;
                &lt;Table.Header&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.HeaderCell&gt;Name&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;Registration Date&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;E-mail address&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;Premium Plan&lt;/Table.HeaderCell&gt;
                    &lt;/Table.Row&gt;
                &lt;/Table.Header&gt;

                &lt;Table.Body&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.Cell&gt;John Lilki&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;September 14, 2013&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;jhlilk22@yahoo.com&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;No&lt;/Table.Cell&gt;
                    &lt;/Table.Row&gt;
                &lt;/Table.Body&gt;
            &lt;/Table&gt;
        &lt;/div&gt;
    )
}
</code></pre><figcaption>read.js</figcaption></figure><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/02/reducedRows.png" class="kg-image" alt="reducedRows" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/02/reducedRows.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/02/reducedRows.png 721w" sizes="(min-width: 720px) 720px" width="721" height="342" loading="lazy"></figure><p>これが Read ページの出力画面です。テーブルには列が 4 つありますが、必要なのは 3 つだけです。</p><p>余分な項目の列を削除してから、次のように項目の名前をつけなおしてください:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/02/reducedColumn.png" class="kg-image" alt="reducedColumn" width="568" height="321" loading="lazy"></figure><p>すると現在、Read ページはこのように見えます:</p><figure class="kg-card kg-code-card"><pre><code>import React from 'react';
import { Table } from 'semantic-ui-react'
export default function Read() {
    return (
        &lt;div&gt;
            &lt;Table singleLine&gt;
                &lt;Table.Header&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.HeaderCell&gt;First Name&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;Last Name&lt;/Table.HeaderCell&gt;
                        &lt;Table.HeaderCell&gt;Checked&lt;/Table.HeaderCell&gt;
                    &lt;/Table.Row&gt;
                &lt;/Table.Header&gt;

                &lt;Table.Body&gt;
                    &lt;Table.Row&gt;
                        &lt;Table.Cell&gt;Nishant&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;Kumar&lt;/Table.Cell&gt;
                        &lt;Table.Cell&gt;Yes&lt;/Table.Cell&gt;
                    &lt;/Table.Row&gt;
                &lt;/Table.Body&gt;
            &lt;/Table&gt;
        &lt;/div&gt;
    )
}
</code></pre><figcaption>read.js</figcaption></figure><p>では、API から GET リクエストを送信してデータを取得してみましょう。</p><p>アプリケーションがマウントされる時に、取得したデータが必要になるので、<code>useEffect</code> フックを使用して実装してください。</p><figure class="kg-card kg-code-card"><pre><code>import React, { useEffect } from 'react';

 useEffect(() =&gt; {
       
 }, [])
</code></pre><figcaption>useEffect フック</figcaption></figure><p>ステートを 1 つ作成して、取得したデータを格納します。取得するデータは配列です。</p><figure class="kg-card kg-code-card"><pre><code>import React, { useEffect, useState } from 'react';

const [APIData, setAPIData] = useState([]);
useEffect(() =&gt; {
       
}, [])
</code></pre><figcaption>取得した API データを格納する APIData ステート</figcaption></figure><p>では、<code>useEffect</code> フックの中で、GET リクエストを送信してみましょう。</p><pre><code> useEffect(() =&gt; {
        axios.get(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData`)
            .then((response) =&gt; {
                setAPIData(response.data);
            })
    }, [])
</code></pre><p>ここでは axios.get を使用して、GET リクエストを API へ送信しています。その後で、リクエストが resolve (成功) すれば、レスポンスデータを <em>APIData</em> ステート内に格納します。</p><p>続いて、取得した API データに応じてテーブルの行をマッピングしましょう。</p><p>そのために Map 関数を使用します。Map 関数は配列を反復処理して、データを出力画面内に表示します。</p><pre><code>&lt;Table.Body&gt;
  {APIData.map((data) =&gt; {
     return (
       &lt;Table.Row&gt;
          &lt;Table.Cell&gt;{data.firstName}&lt;/Table.Cell&gt;
           &lt;Table.Cell&gt;{data.lastName}&lt;/Table.Cell&gt;
           &lt;Table.Cell&gt;{data.checkbox ? 'Checked' : 'Unchecked'}&lt;/Table.Cell&gt;
        &lt;/Table.Row&gt;
   )})}
&lt;/Table.Body&gt;
</code></pre><p>API 内のデータに応じて、firstName, lastName, checkbox のそれぞれのデータをマッピングしています。また、ここでは三項演算子 ('?') を使用しています。data.checkbox が true である場合は、Checked と表示され、そうでない場合は Unchecked と表示されます。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/02/ReadOutput.png" class="kg-image" alt="ReadOutput" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/02/ReadOutput.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/02/ReadOutput.png 674w" width="674" height="401" loading="lazy"><figcaption>read.js の出力画面</figcaption></figure><h3 id="update-">Update 操作</h3><p>それでは、テーブルに Update (更新) 用のヘッダーをもう 1 つ作成し、Update ボタン用の列を、Table.Row の中に作成してください。Update ボタンには、Semantic UI React が提供している Button コンポーネントをインポートして使用してください。</p><figure class="kg-card kg-code-card"><pre><code>&lt;Table.HeaderCell&gt;Update&lt;/Table.HeaderCell&gt;

&lt;Table.Cell&gt; 
  &lt;Button&gt;Update&lt;/Button&gt;
&lt;/Table.Cell&gt;
</code></pre><figcaption>Update ボタンの作成</figcaption></figure><p>ここで、Update ボタンがクリックされた時に、 Update ページにリダイレクトされる必要があります。そのためには React Router が提供している Link が必要です。</p><p>では、React Router から Link をインポートしてください。それから、Link タグの中へ Update ボタン用の Table.Cell をラップしてください。</p><figure class="kg-card kg-code-card"><pre><code>import { Link } from 'react-router-dom';

&lt;Link to='/update'&gt;
  &lt;Table.Cell&gt; 
     &lt;Button&gt;Update&lt;/Button&gt;
   &lt;/Table.Cell&gt;
&lt;/Link&gt;
</code></pre><figcaption>Update ボタン用の Link</figcaption></figure><p>これで、Update ボタンをクリックした時に、Update ページへリダイレクトされるようになりました。</p><p>また、Update ボタンをクリックした時に、各列のデータを更新するには、API が提供している、それぞれの ID が必要です。</p><p><code>setData</code> という名前の関数をコンポーネントの先頭に作成してください。それから、その関数を Update ボタンにバインドしてください。</p><pre><code> &lt;Button onClick={() =&gt; setData()}&gt;Update&lt;/Button&gt;
</code></pre><p>ここでは、先頭の関数へ引数としてデータを渡す必要があります。</p><pre><code> &lt;Button onClick={() =&gt; setData(data)}&gt;Update&lt;/Button&gt;
</code></pre><p>そして、先頭の関数の中で、データをコンソール内に表示させます:</p><pre><code>const setData = (data) =&gt; {
   console.log(data);
}
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/02/updateDataInConsole.png" class="kg-image" alt="updateDataInConsole" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/02/updateDataInConsole.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/02/updateDataInConsole.png 686w" width="686" height="116" loading="lazy"><figcaption>コンソール内のデータ</figcaption></figure><p>では、update ボタンをクリックして、コンソールを確認してみてください。テーブルの各項目のデータを取得しています。</p><p>今度は、このデータをローカルストレージへ格納しましょう。</p><figure class="kg-card kg-code-card"><pre><code>const setData = (data) =&gt; {
        let { id, firstName, lastName, checkbox } = data;
        localStorage.setItem('ID', id);
        localStorage.setItem('First Name', firstName);
        localStorage.setItem('Last Name', lastName);
        localStorage.setItem('Checkbox Value', checkbox);
}
</code></pre><figcaption>ローカルストレージ内へデータを格納</figcaption></figure><p>このようにして、データを id、firstName、lastName、checkbox に分割してから、ローカルストレージへ格納しています。ローカルストレージを使用することで、ブラウザー内でローカルにデータを保存できます。</p><p>今ここで、Update コンポーネント内に update 操作用のフォームが 1 つ必要です。Create コンポーネントで用いたフォームを再利用しましょう。関数名を単に Create から Update へ変更するだけです。</p><figure class="kg-card kg-code-card"><pre><code>import React, { useState } from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react'
import axios from 'axios';

export default function Update() {
    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    const [checkbox, setCheckbox] = useState(false);

    return (
        &lt;div&gt;
            &lt;Form className="create-form"&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;First Name&lt;/label&gt;
                    &lt;input placeholder='First Name' onChange={(e) =&gt; setFirstName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;Last Name&lt;/label&gt;
                    &lt;input placeholder='Last Name' onChange={(e) =&gt; setLastName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;Checkbox label='I agree to the Terms and Conditions' onChange={(e) =&gt; setCheckbox(!checkbox)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Button type='submit'&gt;Update&lt;/Button&gt;
            &lt;/Form&gt;
        &lt;/div&gt;
    )
}
</code></pre><figcaption>Update ページ</figcaption></figure><p><code>useEffect</code> フックを Update コンポーネント内で作成してください。このフックを使い、前もってローカルストレージへ格納したデータを取り出します。さらに、ID 項目用のステートをもう一つ作成します。</p><pre><code>const [id, setID] = useState(null);

useEffect(() =&gt; {
        setID(localStorage.getItem('ID'))
        setFirstName(localStorage.getItem('First Name'));
        setLastName(localStorage.getItem('Last Name'));
        setCheckbox(localStorage.getItem('Checkbox Value'))
}, []);
</code></pre><p>それから、ローカルストレージから取り出したデータを、キーに応じて配置します。フォームの各欄の中に、取り出した値を配置する必要があります。Update ページがロードされた時に、各欄の中にデータが自動的に配置されるようにしましょう。</p><figure class="kg-card kg-code-card"><pre><code>&lt;Form className="create-form"&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;First Name&lt;/label&gt;
                    &lt;input placeholder='First Name' value={firstName} onChange={(e) =&gt; setFirstName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;Last Name&lt;/label&gt;
                    &lt;input placeholder='Last Name' value={lastName} onChange={(e) =&gt; setLastName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;Checkbox label='I agree to the Terms and Conditions' checked={checkbox} onChange={(e) =&gt; setCheckbox(!checkbox)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Button type='submit'&gt;Update&lt;/Button&gt;
            &lt;/Form&gt;
</code></pre><figcaption>フォームの各欄の中に値を配置</figcaption></figure><p>これで、Read ページ内の Update ボタンをクリックすると、Update ページへリダイレクトされ、そこで自動的に配置されたフォームデータを見ることができます。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/02/populatedDataInUpdatePage.png" class="kg-image" alt="populatedDataInUpdatePage" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/02/populatedDataInUpdatePage.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/02/populatedDataInUpdatePage.png 619w" width="619" height="415" loading="lazy"><figcaption>Update ページ</figcaption></figure><p>それでは、Update リクエストを実装してデータを更新してみましょう。</p><p><code>updateAPIData</code> という名前の関数を作成してください。この関数の中で、axios.put を用いて PUT リクエストを送信し、データを更新します。</p><pre><code>const updateAPIData = () =&gt; {
    axios.put(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData/${id}`, {
        firstName,
         lastName,
         checkbox
	})
}
</code></pre><p>ここでは API エンドポイントに id 項目を添えているのが確認できます。</p><p>Read ページで、項目の Update ボタンをクリックした時に、その ID がローカルストレージの中に格納され、その後に Update ページ で取り出した ID を <em><code>id</code></em> ステートの中に格納しています。</p><p>そうした後に、<code>id</code> をエンドポイントへ渡しています。こうすることで、渡している ID の項目を更新できます。</p><p>次に、<code>updateAPIData</code> 関数を Update ボタンにバインドしてください。</p><figure class="kg-card kg-code-card"><pre><code>&lt;Button type='submit' onClick={updateAPIData}&gt;Update&lt;/Button&gt;
</code></pre><figcaption>updateAPIData を Update ボタンにバインド</figcaption></figure><p>Read ページにあるテーブルの中の、Update ボタンをクリックしてから、Last name を変更し、Update ページにある Update ボタンをクリックしてください。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/02/updatingTheFields.png" class="kg-image" alt="updatingTheFields" width="523" height="433" loading="lazy"><figcaption>Last Name 項目の更新</figcaption></figure><p>API を確認してみるか、または Read ページへ戻ってみると、last name が変更されているのを確認できます。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/02/updatedMockAPIData.png" class="kg-image" alt="updatedMockAPIData" width="513" height="28" loading="lazy"><figcaption>変更された Mock API のデータ</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/02/updatedReadTable.png" class="kg-image" alt="updatedReadTable" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/02/updatedReadTable.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/02/updatedReadTable.png 684w" width="684" height="347" loading="lazy"><figcaption>Read ページのテーブル</figcaption></figure><h3 id="delete-">Delete 操作</h3><p>まずは、Read ページのテーブルに、Delete &nbsp;(削除) 操作用のボタンを 1 つ追加しましょう。</p><figure class="kg-card kg-code-card"><pre><code>&lt;Table.HeaderCell&gt;Delete&lt;/Table.HeaderCell&gt;

&lt;Table.Cell&gt;
   &lt;Button&gt;Delete&lt;/Button&gt;
&lt;/Table.Cell&gt;
</code></pre><figcaption>Read ページのテーブル内に Delete ボタンを追加</figcaption></figure><p>次に、<code>onDelete</code> という名前の関数を作成してから、Delete ボタンにその関数をバインドしてください。この関数は、Delete ボタンをクリックした時に ID パラメータを受け取ります。</p><figure class="kg-card kg-code-card"><pre><code>const onDelete = (id) =&gt; {

}

&lt;Table.Cell&gt;
   &lt;Button onClick={() =&gt; onDelete(data.id)}&gt;Delete&lt;/Button&gt;
&lt;/Table.Cell&gt;
</code></pre><figcaption>Delete 操作用の関数</figcaption></figure><p>では、axios.delete を使用して、それぞれの項目を削除する操作を実装しましょう。</p><figure class="kg-card kg-code-card"><pre><code>const onDelete = (id) =&gt; {
  axios.delete(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData/${id}`)
}
</code></pre><figcaption>API から項目を削除</figcaption></figure><p>Delete ボタンをクリックしてから API を確認してみてください。クリックした項目のデータが削除されているのを確認できます。</p><p>ここで、クリックした項目のデータが削除された後に、テーブルのデータをロードし直す必要があります。</p><p>そのために、API のデータを読み込む関数を作成してください。</p><figure class="kg-card kg-code-card"><pre><code>const getData = () =&gt; {
    axios.get(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData`)
        .then((getData) =&gt; {
             setAPIData(getData.data);
         })
}
</code></pre><figcaption>API のデータを取得</figcaption></figure><p>そして <code>onDelete</code> 関数の中で、項目を削除した後に更新された API のデータを読み込む必要があります。</p><figure class="kg-card kg-code-card"><pre><code>const onDelete = (id) =&gt; {
        axios.delete(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData/${id}`)
     .then(() =&gt; {
        getData();
    })
}
</code></pre><figcaption>項目を削除した後に更新された API のデータを読み込む</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/02/ReadTableBeforeDataDeleted.png" class="kg-image" alt="ReadTableBeforeDataDeleted" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/02/ReadTableBeforeDataDeleted.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/02/ReadTableBeforeDataDeleted.png 711w" width="711" height="446" loading="lazy"><figcaption>Read ページのテーブル</figcaption></figure><p>これで、今は各項目の Delete ボタンをクリックすることでその項目が削除され、テーブルが自動的に最新の情報に更新されます。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/02/ReadTableAfterDataDeleted.png" class="kg-image" alt="ReadTableAfterDataDeleted" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/02/ReadTableAfterDataDeleted.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/02/ReadTableAfterDataDeleted.png 673w" width="673" height="335" loading="lazy"><figcaption>項目が 1 つ削除された後の Read ページのテーブル</figcaption></figure><h2 id="crud--2">CRUD アプリを少し改善してみましょう</h2><p>これまで、Create ページの中でデータを投稿 (post) する時には、単にデータを偽のデータベースの中へ入れていましたが、その際に Read ページへリダイレクトする必要があります。</p><p>それではまず、React Router から <code>useHistory</code> をインポートしてください。</p><figure class="kg-card kg-code-card"><pre><code>import { useHistory } from 'react-router';
</code></pre><figcaption>React Router から useHistory をインポート</figcaption></figure><p>次に、<code>let</code> 文で history という名前の変数を作成して、その変数の中に <code>useHistory</code> を格納してください。</p><pre><code>let history = useHistory();
</code></pre><p>その後で、post API が呼び出されてから、POST リクエストが resolve した時に、history.push 関数を使用して Read ページへ移動します。</p><figure class="kg-card kg-code-card"><pre><code>const postData = () =&gt; {
        axios.post(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData`, {
            firstName,
            lastName,
            checkbox
        }).then(() =&gt; {
            history.push('/read')
        })
    }
</code></pre><figcaption>Post API が resolve した後で Read ページへ移動する</figcaption></figure><p>このように <code>useHistory</code> フックを用いることで Read ページへ移動できます。</p><p>Update ページについても同じことを行ってください。</p><figure class="kg-card kg-code-card"><pre><code>import React, { useState, useEffect } from 'react';
import { Button, Checkbox, Form } from 'semantic-ui-react'
import axios from 'axios';
import { useHistory } from 'react-router';

export default function Update() {
    let history = useHistory();
    const [id, setID] = useState(null);
    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    const [checkbox, setCheckbox] = useState(false);

    useEffect(() =&gt; {
        setID(localStorage.getItem('ID'))
        setFirstName(localStorage.getItem('First Name'));
        setLastName(localStorage.getItem('Last Name'));
        setCheckbox(localStorage.getItem('Checkbox Value'));
    }, []);

    const updateAPIData = () =&gt; {
        axios.put(`https://60fbca4591156a0017b4c8a7.mockapi.io/fakeData/${id}`, {
            firstName,
            lastName,
            checkbox
        }).then(() =&gt; {
            history.push('/read')
        })
    }
    return (
        &lt;div&gt;
            &lt;Form className="create-form"&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;First Name&lt;/label&gt;
                    &lt;input placeholder='First Name' value={firstName} onChange={(e) =&gt; setFirstName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;label&gt;Last Name&lt;/label&gt;
                    &lt;input placeholder='Last Name' value={lastName} onChange={(e) =&gt; setLastName(e.target.value)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Form.Field&gt;
                    &lt;Checkbox label='I agree to the Terms and Conditions' checked={checkbox} onChange={() =&gt; setCheckbox(!checkbox)}/&gt;
                &lt;/Form.Field&gt;
                &lt;Button type='submit' onClick={updateAPIData}&gt;Update&lt;/Button&gt;
            &lt;/Form&gt;
        &lt;/div&gt;
    )
}
</code></pre><figcaption>Update.js</figcaption></figure><p>以上で、React と React フックを使用して CRUD 操作を実行する方法について理解できました！</p><p>学習内容を補足したい場合は、<a href="https://youtu.be/-ZMP8ZladIQ">React CRUD Operations</a> という YouTube のビデオ を、この記事の代わりに見ることができます。</p><p>さらに、コードをテストしてみたい場合は、<a href="https://github.com/nishant-666/React-CRUD-Operation-V2">GitHubでコードを見つける</a>ことができます。</p><blockquote><strong>ハッピーラーニング</strong></blockquote> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React で Axios を使用する方法とは: 決定版ガイド (2021年) ]]>
                </title>
                <description>
                    <![CDATA[ このガイドでは、React フックを盛り込んだ多くの実例を使用することによって、React で Axios.js を正しく使用する方法がわかります。 まず、なぜデータの取り込みライブラリとして Axios を使用すべきなのかについてお伝えし、React で Axios を設定する方法と、主要な 4 つのタイプの HTTP リクエストを Axios で送信する方法について見ていきます。 その後、再利用性を良くするために Axios インスタンスを作成し、わかりやすくするために async-await を使用する方法や、カスタムフックとして Axios を使用する方法など、より高度な機能について触れます。 さっそくはじめましょう！ プログラムの写しが必要ですか?‬ 📄 PDF 形式のチートシートをダウンロードするにはこちらをクリックしてください [https://reedbarger.com/resources/react-axios-2021] (5 秒で終わります)。 このチートシートは、この記事の重要な情報をすべて含んでいる、便利な PDF ガイドです。 目次  * A ]]>
                </description>
                <link>https://www.freecodecamp.org/japanese/news/how-to-use-axios-with-react/</link>
                <guid isPermaLink="false">61dbd7a3c6ab600507251760</guid>
                
                    <category>
                        <![CDATA[ AXIOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manabu Matsumoto ]]>
                </dc:creator>
                <pubDate>Wed, 23 Feb 2022 23:45:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/japanese/news/content/images/2022/01/how-to-use-axios-with-react.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文:</strong> <a href="https://www.freecodecamp.org/news/how-to-use-axios-with-react/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How To Use Axios With React: The Definitive Guide (2021)</a>
      </p><p>このガイドでは、React フックを盛り込んだ多くの実例を使用することによって、React で Axios.js を正しく使用する方法がわかります。</p><p>まず、なぜデータの取り込みライブラリとして Axios を使用すべきなのかについてお伝えし、React で Axios を設定する方法と、主要な 4 つのタイプの HTTP リクエストを Axios で送信する方法について見ていきます。</p><p>その後、再利用性を良くするために Axios インスタンスを作成し、わかりやすくするために async-await を使用する方法や、カスタムフックとして Axios を使用する方法など、より高度な機能について触れます。</p><p>さっそくはじめましょう！</p><h3 id="-"><strong>プログラムの写しが必要ですか?‬ 📄</strong></h3><p><a href="https://reedbarger.com/resources/react-axios-2021"><strong><strong>PDF 形式のチートシートをダウンロードするにはこちらをクリックしてください</strong></strong></a> (5 秒で終わります)。</p><p>このチートシートは、この記事の重要な情報をすべて含んでいる、便利な PDF ガイドです。</p><h2 id="--1">目次</h2><ul><li><a href="#axios-">Axios とは?</a></li><li><a href="#-react-axios-">なぜ React の中で Axios を用いるのか？</a></li><li><a href="#react-axios-">React で Axios を設定する方法</a></li><li><a href="#get-">GET リクエストを送信する方法 (データの取得)</a></li><li><a href="#post-">POST リクエストを送信する方法 (データの作成)</a></li><li><a href="#put-">PUT リクエストを送信する方法 (データの更新)</a></li><li><a href="#delete-">DELETE リクエストを送信する方法 (データの削除)</a></li><li><a href="#axios--1">Axios でエラーを扱う方法</a></li><li><a href="#axios--2">Axios インスタンスを作成する方法</a></li><li><a href="#axios-async-await-">Axios で Async-Await 構文を使用する方法</a></li><li><a href="#-useaxios-">カスタム <code>useAxios</code> フックを作成する方法</a></li></ul><h2 id="axios-">Axios とは？</h2><p>Axios とは、特定のエンドポイントへのリクエストを送信できるようにする、HTTP クライアントライブラリです:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/Screen-Shot-2021-07-12-at-1.14.41-PM-2.png" class="kg-image" alt="Screen-Shot-2021-07-12-at-1.14.41-PM-2" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/Screen-Shot-2021-07-12-at-1.14.41-PM-2.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/Screen-Shot-2021-07-12-at-1.14.41-PM-2.png 1000w" sizes="(min-width: 720px) 720px" width="1000" height="731" loading="lazy"></figure><p>たとえば、外部 API や独自のバックエンド Node.js サーバーを、Axios で実装できます。</p><p>リクエストを送信することで、API に、そのリクエストに応じた処理の実行を要求します。</p><p>たとえば、GET リクエストを送信した場合は、アプリケーションの中で表示するデータを返信するように要求します。</p><h2 id="-react-axios-">なぜ React の中で Axios を用いるのか？</h2><p>HTTP リクエストの送信に使用できる、さまざまなライブラリが数多く存在しているにもかかわらず、なぜ Axios を選択するのでしょうか？</p><p>HTTP リクエストを送信するクライアントとして、 Axios を使用すべき <strong>5 つの理由</strong>を、以下に挙げます:</p><ol><li>Axios は、初期設定で JSON 形式のデータをうまく扱うことができます。Fetch API などの別のものと違い、多くの場合はヘッダーを設定する必要がありません。また、リクエストボディを JSON 文字列へ変換するといった、面倒な作業を行う必要がありません。</li><li>Axios には、主な HTTP メソッドと一致する名前の関数が用意されています。GET リクエストを送信する時は、<code>.get()</code> メソッドを使用します。</li><li>Axios は、より少ないコードでより多くのことを行います。Fetch API と違い、一度だけ <code>.then()</code> コールバックを用いれば、リクエストした JSON データにアクセスできます。</li><li>Axios にはより良いエラー処理機能があります。ステータスコードをチェックして、エラーを自分で返さなければならない Fetch API と違い、Axios は 400 番台と 500 番台のエラーレスポンスステータスコードを返します。</li><li>Axios は、クライアント側でもサーバー側でも使用できます。Node.js でアプリケーションのコードを記述している場合は、ブラウザーとは別の環境の中でも Axios を使用できることに注意してください。</li></ol><h2 id="react-axios-">React で Axios を設定する方法</h2><p>React で Axios を使用する手順はとても簡単です。以下の 3 つのことが必要です:</p><ol><li>React のプロジェクトを作成してあること</li><li>npm または yarn で Axios をインストールすること</li><li>リクエストを送信するための API エンドポイント</li></ol><p>新しい React アプリケーションを最も早く作成する方法は、<a href="https://react.new">react.new</a> を訪れることです。</p><p>すでに React のプロジェクトがある場合は、Axios を npm (または他のパッケージマネージャー) でインストールする必要があるだけです:</p><pre><code class="language-bash">npm install axios
</code></pre><p>また、このガイドでは JSON Placeholder API を使って、投稿 (post) データを取得し、変更します。</p><p>リクエストを送信できる全てのルートについて、それぞれにふさわしい HTTP メソッドと一緒に記載したものの一覧がこちらです:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/Screen-Shot-2021-07-10-at-12.21.28-PM-2.png" class="kg-image" alt="Screen-Shot-2021-07-10-at-12.21.28-PM-2" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/Screen-Shot-2021-07-10-at-12.21.28-PM-2.png 600w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/Screen-Shot-2021-07-10-at-12.21.28-PM-2.png 617w" width="617" height="386" loading="lazy"></figure><p>Axios と API のエンドポイントを使用して行う操作 (投稿の取得、作成、更新、削除) の手短な例がこちらです:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/axios-react-2.gif" class="kg-image" alt="axios-react-2" width="1920" height="1080" loading="lazy"></figure><h2 id="get-">GET リクエストを送信する方法</h2><p>データを取得したり検索するには、GET リクエストを送信します。</p><p>最初に、個々の投稿に対してリクエストを送信してみましょう。エンドポイントを見てみると、 <code>/posts/1</code> エンドポイントから最初の投稿を取得できます:</p><pre><code class="language-js">import axios from "axios";
import React from "react";

const baseURL = "https://jsonplaceholder.typicode.com/posts/1";

export default function App() {
  const [post, setPost] = React.useState(null);

  React.useEffect(() =&gt; {
    axios.get(baseURL).then((response) =&gt; {
      setPost(response.data);
    });
  }, []);

  if (!post) return null;

  return (
    &lt;div&gt;
      &lt;h1&gt;{post.title}&lt;/h1&gt;
      &lt;p&gt;{post.body}&lt;/p&gt;
    &lt;/div&gt;
  );
}
</code></pre><p><code>useEffect</code> フックを使って、コンポーネントがマウントされた時点でこのリクエストを送信します。これには Axios をインポートし、<code>.get()</code> メソッドを用いて GET リクエストをエンドポイントへ送信し、<code>.then()</code> コールバックを用いてすべてのレスポンスデータを取得することが伴います。</p><p>レスポンスはオブジェクトとして返されます。データ (この場合、 <code>id</code>、<code>title</code>、<code>body</code> プロパティーを伴う投稿データ) は <code>post</code> と呼ばれるステートに格納され、コンポーネントの中で表示されます。</p><p>レスポンスの中の <code>.data</code> プロパティーから、常にリクエストされたデータを見つけることができるということに注意してください。</p><h2 id="post-">POST リクエストを送信する方法</h2><p>新しいデータを作成するには、POST リクエストを送信します。</p><p>JSON Placeholder API の仕様に従い、POST リクエストを <code>/posts</code> エンドポイントに送信する必要があります。以下のコードを見てみると、投稿データを作成するボタンがあるのを確認できます:</p><pre><code class="language-js">import axios from "axios";
import React from "react";

const baseURL = "https://jsonplaceholder.typicode.com/posts";

export default function App() {
  const [post, setPost] = React.useState(null);

  React.useEffect(() =&gt; {
    axios.get(`${baseURL}/1`).then((response) =&gt; {
      setPost(response.data);
    });
  }, []);

  function createPost() {
    axios
      .post(baseURL, {
        title: "Hello World!",
        body: "This is a new post."
      })
      .then((response) =&gt; {
        setPost(response.data);
      });
  }

  if (!post) return "No post!"

  return (
    &lt;div&gt;
      &lt;h1&gt;{post.title}&lt;/h1&gt;
      &lt;p&gt;{post.body}&lt;/p&gt;
      &lt;button onClick={createPost}&gt;Create Post&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre><p>ボタンをクリックすると、<code>createPost</code> 関数を呼び出します。</p><p>Axios で POST リクエストを送信するには、<code>.post()</code> メソッドを使用します。作成しようとしている新しい投稿データを、オブジェクトのプロパティーとして記述し、第二引数として渡します。</p><p>もう一度 <code>.then()</code> コールバックを使ってレスポンスデータを取得し、最初に取得した投稿を、リクエストした新しい投稿 に置き換えます。</p><p>‌‌これは <code>.get()</code> メソッドとよく似ていますが、作成しようとしている新しいリソースが、API エンドポイントの後ろに第二引数として渡されています。</p><h2 id="put-">PUT リクエストを送信する方法</h2><p>特定のリソースを更新するには、PUT リクエストを送信します。</p><p>以下の例では、最初の投稿を更新します。</p><p>そのためにもう一度ボタンを作成しましょう。ただし今回は、投稿を更新するための関数を、ボタンで呼び出します:</p><pre><code class="language-js">import axios from "axios";
import React from "react";

const baseURL = "https://jsonplaceholder.typicode.com/posts";

export default function App() {
  const [post, setPost] = React.useState(null);

  React.useEffect(() =&gt; {
    axios.get(`${baseURL}/1`).then((response) =&gt; {
      setPost(response.data);
    });
  }, []);

  function updatePost() {
    axios
      .put(`${baseURL}/1`, {
        title: "Hello World!",
        body: "This is an updated post."
      })
      .then((response) =&gt; {
        setPost(response.data);
      });
  }

  if (!post) return "No post!"

  return (
    &lt;div&gt;
      &lt;h1&gt;{post.title}&lt;/h1&gt;
      &lt;p&gt;{post.body}&lt;/p&gt;
      &lt;button onClick={updatePost}&gt;Update Post&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre><p>上のコードの中では、Axois の PUT メソッドを使用します。POST メソッドの場合と同じように、更新しようとしているリソースをオブジェクトのプロパティーとして記述し、第二引数として渡しています。</p><p>ここでまた <code>.then()</code> コールバックを用いて、返ってきたデータで JSX を更新します。</p><h2 id="delete-">DELETE リクエストを送信する方法</h2><p>最後に、リソースを削除するために、DELETE メソッドを使用します。</p><p>その例として、最初の投稿を削除してみましょう。</p><p>このリクエストを送信する時に、第二引数には何も必要ないことに注意してください:</p><pre><code class="language-js">import axios from "axios";
import React from "react";

const baseURL = "https://jsonplaceholder.typicode.com/posts";

export default function App() {
  const [post, setPost] = React.useState(null);

  React.useEffect(() =&gt; {
    axios.get(`${baseURL}/1`).then((response) =&gt; {
      setPost(response.data);
    });
  }, []);

  function deletePost() {
    axios
      .delete(`${baseURL}/1`)
      .then(() =&gt; {
        alert("Post deleted!");
        setPost(null)
      });
  }

  if (!post) return "No post!"

  return (
    &lt;div&gt;
      &lt;h1&gt;{post.title}&lt;/h1&gt;
      &lt;p&gt;{post.body}&lt;/p&gt;
      &lt;button onClick={deletePost}&gt;Delete Post&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre><p>ほとんどの場合、<code>.delete()</code> メソッドから返ってきたデータは必要ありません。</p><p>ただし上のコードの中では、リクエストが正常に resolve (成功) したことを確かめるために、<code>.then()</code> コールバックがさらに用いられています。</p><p>上のコードの中で、投稿が削除された後に、ユーザーはうまく削除されたことを通知されます。それから、ステートの <code>post</code> の値が、初期値である <code>null</code> に設定されます。</p><p>また、一度投稿が削除されると、「No post」というテキストがアラートメッセージの後にすぐに表示されます。</p><h2 id="axios--1">Axios でエラーを扱う方法</h2><p>Axios でエラーを扱うとはどういうことについて言うのでしょうか？</p><p>リクエストの送信中にエラーが発生するとどうなるでしょうか？例えば、誤ったデータを送信したり、誤ったエンドポイントへリクエストを送信したり、ネットワークエラーが発生したりする場合があるかもしれません。</p><p>エラーのシミュレーションを行うために、<code>/posts/asdf</code> というような存在しない API エンドポイントにリクエストを送信してみましょう。</p><p>このリクエストを送信すると、<code>404</code> ステータスコードが返ってきます:</p><pre><code class="language-js">import axios from "axios";
import React from "react";

const baseURL = "https://jsonplaceholder.typicode.com/posts";

export default function App() {
  const [post, setPost] = React.useState(null);
  const [error, setError] = React.useState(null);

  React.useEffect(() =&gt; {
    // invalid url will trigger an 404 error
    axios.get(`${baseURL}/asdf`).then((response) =&gt; {
      setPost(response.data);
    }).catch(error =&gt; {
      setError(error);
    });
  }, []);
  
  if (error) return `Error: ${error.message}`;
  if (!post) return "No post!"

  return (
    &lt;div&gt;
      &lt;h1&gt;{post.title}&lt;/h1&gt;
      &lt;p&gt;{post.body}&lt;/p&gt;
    &lt;/div&gt;
  );
}
</code></pre><p>この場合、<code>.then()</code> コールバックを実行する代わりに、Axios はエラーを返して、<code>.catch()</code> コールバック関数を実行します。</p><p>この関数の中で、エラーデータを取得してステートに保存することで、エラーについてユーザーに通知します。つまり、エラーが発生したらエラーメッセージを表示します。</p><p>このコードを実行したときに、「Error: Request failed with status code 404」というテキストが見えます。</p><h2 id="axios--2">Axios インスタンスを作成する方法</h2><p>これまでの例を見てみると、各リクエストを送信するために用いる Axios のエンドポイントの一部として、 <code>baseURL</code> があるのを確認できます。</p><p>とはいえ、すべてのリクエストに対してひとつずつ <code>baseURL</code> を記述し続けるのはちょっと面倒ですよね。いつも似たようなエンドポイントを含んでいるのですから、使用している <code>baseURL</code> を、単に Axios に覚えさせることはできないものでしょうか？</p><p>実はできます。<code>.create()</code> メソッドでインスタンスを作成した場合、Axios は <code>baseURL</code> を記憶し、さらに、すべてのリクエストに対して指定し得る、ヘッダーを含めた他の値を記憶します:</p><pre><code class="language-js">import axios from "axios";
import React from "react";

const client = axios.create({
  baseURL: "https://jsonplaceholder.typicode.com/posts" 
});

export default function App() {
  const [post, setPost] = React.useState(null);

  React.useEffect(() =&gt; {
    client.get("/1").then((response) =&gt; {
      setPost(response.data);
    });
  }, []);

  function deletePost() {
    client
      .delete("/1")
      .then(() =&gt; {
        alert("Post deleted!");
        setPost(null)
      });
  }

  if (!post) return "No post!"

  return (
    &lt;div&gt;
      &lt;h1&gt;{post.title}&lt;/h1&gt;
      &lt;p&gt;{post.body}&lt;/p&gt;
      &lt;button onClick={deletePost}&gt;Delete Post&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre><p>上のコードでは、設定用オブジェクトのプロパティーは <code>baseURL</code> だけであり、それにエンドポイントを渡しています。</p><p>この場合では、<code>.create()</code> 関数は <code>client</code> と呼ばれる、新しく作られたインスタンスを返します。</p><p>そうすることで、今後は、これまでと同じすべてのメソッドを用いるときに、第一引数として <code>baseURL</code> を渡す必要が無くなります。例えば、<code>/</code>、<code>/1</code> などの必要な特定のルートを、単に参照するだけで済みます。</p><h2 id="axios-async-await-">Axios で Async-Await 構文を使用する方法</h2><p>JavaScript (React アプリケーションを含む) の中でプロミスを用いることの大きな利点は、async-await 構文が使用できることです。</p><p>async-await は、より簡潔なコードを記述できるようにしてくれます。さらに、async-await を用いたコードは、同期コードにとてもよく似ていますし、わかりやすいです。</p><p>では、どのようにして Axios で async-await 構文を用いればよいのでしょうか？</p><p>以下の例において、投稿が取得されてから、さらにその投稿を削除するボタンがあります:</p><pre><code class="language-js">import axios from "axios";
import React from "react";

const client = axios.create({
  baseURL: "https://jsonplaceholder.typicode.com/posts" 
});

export default function App() {
  const [post, setPost] = React.useState(null);

  React.useEffect(() =&gt; {
    async function getPost() {
      const response = await client.get("/1");
      setPost(response.data);
    }
    getPost();
  }, []);

  async function deletePost() {
    await client.delete("/1");
    alert("Post deleted!");
    setPost(null);
  }

  if (!post) return "No post!"

  return (
    &lt;div&gt;
      &lt;h1&gt;{post.title}&lt;/h1&gt;
      &lt;p&gt;{post.body}&lt;/p&gt;
      &lt;button onClick={deletePost}&gt;Delete Post&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre><p>ここでは <code>useEffect</code> の中に、<code>getPost</code> と呼ばれる <code>async</code> 関数があります。</p><p>関数を <code>async</code> にしたことにより、<code>await</code> キーワードを用いて GET リクエストを resolve させ、次の行でそのデータをステートに設定することが可能になりました。これで <code>.then()</code> コールバックを使わずに書くことができます。</p><p><code>getPost</code> 関数が、作成された後ですぐに呼び出されていることに注意してください。</p><p>さらに、<code>deletePost</code> 関数も <code>async</code> になっています。これは、promise を resolve する <code>await</code> キーワードを使用するために必要です (Axios メソッドはすべて、resolve すべき promise を返します) 。</p><p>‌‌DELETE リクエストと一緒に <code>await</code> キーワードを使用した後で、ユーザーは投稿が削除されたことを通知され、ステートの <code>post</code> の値を <code>null</code> に設定します。</p><p>ご覧になったとおり、async-await はコードを大幅に整理し、とても簡単に、Axios と一緒に使用できます。</p><h2 id="-useaxios-">カスタム <code>useAxios</code> フックを作成する方法</h2><p>async-await は、コードをわかりやすくする素晴らしい方法ですが、これをさらにもう一歩進めることができます。</p><p>コンポーネントがマウントされる際に <code>useEffect</code> を用いてデータを取得する代わりに、Axios で同じ処理を実行できる独自のカスタムフックを、再利用できる関数として作成できます。</p><p>カスタムフックは自分で作れますが、その一方で、use-axois-client と呼ばれるとても良いライブラリが、カスタム <code>useAxios</code> フックを提供しています。</p><p>まずは、パッケージをインストールします:</p><pre><code>npm install use-axios-client
</code></pre><p>フックを使用するために、JavaScript ファイルの先頭で、use-axios-client から <code>useAxios</code> をインポートします。</p><p>もう <code>useEffect</code> は必要ないため、React のインポートは削除できます:</p><pre><code class="language-js">import { useAxios } from "use-axios-client";

export default function App() {
  const { data, error, loading } = useAxios({
    url: "https://jsonplaceholder.typicode.com/posts/1"
  });

  if (loading || !data) return "Loading...";
  if (error) return "Error!";

  return (
    &lt;div&gt;
      &lt;h1&gt;{data.title}&lt;/h1&gt;
      &lt;p&gt;{data.body}&lt;/p&gt;
    &lt;/div&gt;
  ) 
}
</code></pre><p>これで、<code>useAxios</code> を App コンポーネントの先頭で呼び出してから、リクエストを送信しようとしている URL を渡すことができます。また、このフックは、いろいろな状態を扱うのに必要となる、すべての値 (<code>loading</code>、<code>error</code>、そして resolve した <code>data</code>) を含むオブジェクトを返します。</p><p>このリクエストの実行中は、 <code>loading</code> の値は true です。エラーが発生した場合は、エラー状態を表示しましょう。そうでなければ、返ってきたデータがある場合は UI (ユーザーインターフェース) の中でそれを表示できます。</p><p>このようなカスタムフックの利点は、コードを実際に削減し、全体的に簡素化できることです。</p><p>Axios でより単純にデータを取得する方法を探しているなら、試しに今回のようにカスタム <code>useAxios</code> フックを使ってみてください。</p><h2 id="--2">その次は？</h2><p>おめでとうございます！ 最も強力な HTTP クライアントライブラリのひとつを使用して、React アプリケーションを強化する方法を今、知ることができました。</p><p>このガイドから、多くのことを学んでいただけるよう願っています。</p><p><a href="https://reedbarger.com/resources/react-axios-2021">後で参照できるように、このガイドを PDF チートシートとしてダウンロードできることを覚えておいてください。</a></p><h2 id="-react-bootcamp-">さらに多くの学びをご希望でしょうか？ React Bootcamp にご参加ください</h2><p><a href="https://reactbootcamp.com/"><strong>React Bootcamp</strong></a> は、Reactの学習について知っておくべきことをすべて取り入れ、ビデオ、チートシート、特別なボーナスなどを、1つのわかりやすいパッケージにまとめています。</p><p>React のプロになり、夢の仕事を得て、自分の未来を管理するために、<strong>何百人もの開発者</strong>がすでに使用している内部関係者向けの情報をこちらで手に入れてください:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/japanese/news/content/images/2022/01/react-bootcamp-1.png" class="kg-image" alt="react-bootcamp-1" srcset="https://www.freecodecamp.org/japanese/news/content/images/size/w600/2022/01/react-bootcamp-1.png 600w, https://www.freecodecamp.org/japanese/news/content/images/size/w1000/2022/01/react-bootcamp-1.png 1000w, https://www.freecodecamp.org/japanese/news/content/images/2022/01/react-bootcamp-1.png 1590w" sizes="(min-width: 1200px) 1200px" width="1590" height="567" loading="lazy"></figure><p><em><a href="http://bit.ly/join-react-bootcamp">通知を受け取るにはこちらをクリックしてください</a></em></p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
