Node.js, React、および Websockets を使用したカンバンボードの構築 📝 ✨

この記事の内容とは?

この記事では、JIRA、Monday、および Trello にあるカンバンボードを、React、Socket.io、および React Beautiful DNDを使用してドラッグ・アンド・ドロップ機能を使って見た目も美しいものを作成する方法を学びます。ユーザーはサインインし、様々なタスクの作成や更新、コメントの追加ができるようになります。

デモ

Novu - 最初のオープンソース通知インフラストラクチャ

私たちのことを少し紹介させてください。Novuは最初のオープンソース通知インフラストラクチャです。私たちは、すべての製品通知の管理を支援します。それはアプリ内(DEV Communityにあるベルアイコン - Websockets)、Eメール、SMS などが含まれます。

もしスターをつけていただけるととても嬉しいです!それによって私は毎週もっと記事を作成することができます 🚀
https://github.com/novuhq/novu

Novu
Hacktoberfestの期間中には素敵なスワッグも送ります 😇

Socket.ioとは何か?

Socket.io は、WebブラウザとNode.jsサーバー間でリアルタイム、双方向通信を実現するための人気のあるJavaScriptライブラリです。これは、最小限の遅延で大量のデータを処理するために最適化された高性能で信頼性のあるライブラリです。WebSocketプロトコルに従っており、HTTPロングポーリングへのフォールバックや自動再接続など、より優れた機能を提供し、効率的なリアルタイムアプリケーションを構築することを可能にします。

isthereajira

Socket.io および React.js でリアルタイム接続を作成する方法

ここでは、プロジェクト環境のセットアップを行います。また、ReactおよびNode.jsアプリケーションにSocket.ioを追加し、両方の開発サーバーをSocket.ioを介してリアルタイム通信で接続する方法についても学びます。

プロジェクトフォルダを作成し、クライアントとサーバという名称の2つのサブフォルダを含めます。

mkdir todo-list
cd todo-list
mkdir client server

端末を介してクライアントフォルダに移動し、新しいReact.jsプロジェクトを作成します。

cd client
npx create-react-app ./

Socket.ioクライアントAPIおよびReact Routerをインストールします。React Routerは、Reactアプリケーション内でページ間をナビゲートすることを可能にするJavaScriptライブラリです。

npm install socket.io-client react-router-dom

冗長なファイルをReactアプリから削除し、下記のようにApp.jsファイルを更新して"Hello World"を表示させます。

function App() {
    return (
        <div>
            <p>Hello World!</p>
        </div>
    );
}
export default App;

サーバフォルダに移動し、package.jsonファイルを作成します。

cd server & npm init -y

Express.js、CORS、Nodemon、およびSocket.ioサーバAPIをインストールします。

Express.jsは、Node.jsでWebアプリケーションを構築するための機能を数多く提供する高速でミニマリストのフレームワークです。CORSは、異なるドメイン間の通信を許可するNode.jsパッケージです。

Nodemonは、ファイル変更を検出した後に自動的にサーバーを再起動するNode.jsツールであり、Socket.ioにより、サーバー上でリアルタイム接続を構成できます。

npm install express cors nodemon socket.io

index.jsファイルを作成します。これはWebサーバーへのエントリーポイントです。

touch index.js

Express.jsを使用してシンプルなNode.jsサーバをセットアップします。以下のコードスニペットは、ブラウザでhttp://localhost:4000/apiにアクセスするとJSONオブジェクトを返します。

//👇🏻index.js
const express = require("express");
const app = express();
const PORT = 4000;

app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.get("/api", (req, res) => {
    res.json({
        message: "Hello world",
    });
});

app.listen(PORT, () => {
    console.log(`Server listening on ${PORT}`);
});

HTTPおよびCORSライブラリをインポートして、クライアントとサーバドメイン間のデータ転送を許可します。

const express = require("express");
const app = express();
const PORT = 4000;

app.use(express.urlencoded({ extended: true }));
app.use(express.json());

//New imports
const http = require("http").Server(app);
const cors = require("cors");

app.use(cors());

app.get("/api", (req, res) => {
    res.json({
        message: "Hello world",
    });
});

http.listen(PORT, () => {
    console.log(`Server listening on ${PORT}`);
});

次に、リアルタイム接続を作成するためにプロジェクトにSocket.ioを追加します。app.get()ブロックの前に以下のコードをコピーします。

//New imports
.....
const socketIO = require('socket.io')(http, {
    cors: {
        origin: "http://localhost:3000"
    }
});

//Add this before the app.get() block
socketIO.on('connection', (socket) => {
    console.log(`⚡: ${socket.id} user just connected!`);
    socket.on('disconnect', () => {
      socket.disconnect()
      console.log('🔥: A user disconnected');
    });
});

上記のコードスニペットから、socket.io("connection")関数はReactアプリとの接続を確立し、ソケットごとに一意のIDを作成し、ユーザーがWebページを訪問するたびにコンソールにそのIDを記録します。

Webページをリフレッシュまたは閉じると、ソケットが切断イベントを発火し、ユーザーがソケットから切断されたことを示します。

package.jsonファイルのスクリプトリストにスタートコマンドを追加してNodemonを設定します。以下のコードスニペットは、Nodemonを使用してサーバを起動します。

//In server/package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon index.js"
},

次のコマンドを使用してNodemonでサーバを実行できます。

npm start

ユーザーインターフェースの構築

ここでは、アプリケーションのユーザーインターフェースを作成します。これは3つのページに分かれています:ログインページ、タスクページ(アプリケーションの中心部分)、そしてコメントページ(各タスクにユーザがコメントできるページ)です。

client/srcにナビゲートし、Login.jsTask.jsComments.jsファイルを含むコンポーネントフォルダを作成します。

cd client/src
mkdir components
cd components
touch Login.js Task.js Comments.js

React Routerを介して異なるルート上で新しく作成したコンポーネントをレンダリングするためにApp.jsファイルを更新します。

import { BrowserRouter, Route, Routes } from "react-router-dom";
import Comments from "./components/Comments";
import Task from "./components/Task";
import Login from "./components/Login";

function App() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path='/' element={<Login />} />
                <Route path='/task' element={<Task />} />
                <Route path='/comments/:category/:id' element={<Comments />} />
            </Routes>
        </BrowserRouter>
    );
}

export default App;

src/index.cssファイルに次のコードをコピーしましょう。プロジェクトのために必要な全てのCSSが含まれています。

/* CSSコードをここに挿入 */

ログインページ

ここでは、アプリケーションがユーザー名を受け入れてローカルストレージに保存し、識別に使用します。

Login.jsファイルを以下のように更新します。

import React, { useState } from "react";
import { useNavigate } from "react-router-dom";

const Login = () => {
    const [username, setUsername] = useState("");
    const navigate = useNavigate();

    const handleLogin = (e) => {
        e.preventDefault();
        //👇🏻 saves the username to localstorage
        localStorage.setItem("userId", username);
        setUsername("");
        //👇🏻 redirects to the Tasks page.
        navigate("/tasks");
    };
    return (
        <div className='login__container'>
            <form className='login__form' onSubmit={handleLogin}>
                <label htmlFor='username'>Provide a username</label>
                <input
                    type='text'
                    name='username'
                    id='username'
                    required
                    onChange={(e) => setUsername(e.target.value)}
                    value={username}
                />
                <button>SIGN IN</button>
            </form>
        </div>
    );
};

export default Login;

タスクページ

ここで、TaskページのWebレイアウトを作成します。下の画像はページのレイアウトを表しています。

タスクページ

レイアウトはNav.jsAddTask.js(フォーム入力セクション)、およびタスクを含むTasksContainer.jsという3つのコンポーネントに分割します。

cd src/components
touch Nav.js AddTask.js TasksContainer.js

Task.jsファイル内でコンポーネントをレンダリングします。

import React from "react";
import AddTask from "./AddTask";
import TasksContainer from "./TasksContainer";
import Nav from "./Nav";
import socketIO from "socket.io-client";

/*
👇🏻  Pass Socket.io into the required components
    where communications are made with the server
*/
const socket = socketIO.connect("http://localhost:4000");

const Task = () => {
    return (
        <div>
            <Nav />
            <AddTask socket={socket} />
            <TasksContainer socket={socket} />
        </div>
    );
};

export default Task;

Nav.jsファイルに以下のコードをコピーします。

import React from "react";

const Nav = () => {
    return (
        <nav className='navbar'>
            <h3>Team's todo list</h3>
        </nav>
    );
};
export default Nav;

AddTask.jsファイルを以下のように更新します。

import React, { useState } from "react";

const AddTask = ({ socket }) => {
    const [task, setTask] = useState("");

    const handleAddTodo = (e) => {
        e.preventDefault();
        //👇🏻 Logs the task to the console
        console.log({ task });
        setTask("");
    };
    return (
        <form className='form__input' onSubmit={handleAddTodo}>
            <label htmlFor='task'>Add Todo</label>
            <input
                type='text'
                name='task'
                id='task'
                value={task}
                className='input'
                required
                onChange={(e) => setTask(e.target.value)}
            />
            <button className='addTodoBtn'>ADD TODO</button>
        </form>
    );
};

export default AddTask;

TasksContainer.jsファイルに以下のコードをコピーします。これによって、ペンディング、進行中、完了の各タスクのための三つの親要素がレンダリングされます。

/* Reactコンポーネントをここに挿入 */

やったね!💃🏻 レイアウトが整いました。それではコメントページの簡素なテンプレートを作成しましょう。

コメントページ

Comments.jsファイルに以下のコードをコピーします。これによって、コメントとユーザー名がコンソールに記録されます。

/* Reactコンポーネントをここに挿入 */

ユーザーインターフェースが完成しました。次に、React Beautiful DNDをアプリケーションに追加して、ドラッグ・アンド・ドロップ機能を可能にしましょう。

React Beautiful DNDを使ってドラッグ・アンド・ドロップ機能を追加する方法

ここでは、React Beautiful DNDを使ってドラッグ・アンド・ドロップ機能を追加し、ReactアプリとSocket.io Node.jsサーバー間の通信を行う方法を学びます。

React Beautiful DNDの仕組み

React Beautiful DNDは、アイテムを現在の位置からページ上の別の位置に選択してドラッグすることができる高性能ライブラリです。

React Beautiful DND

上の画像は、React Beautiful DNDのセットアップ方法を説明しています。全てのドラッグ可能およびドロップ可能なアイテムを<DragDropContext/>内にラップする必要があります。<Droppable/>コンポーネントは、<Draggable/>コンポーネント内に配置されたドラッグ可能なアイテムを保持します。

こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
https://dev.to/novu/building-a-beautiful-kanban-board-with-nodejs-react-and-websockets-39dk