 
					雑記
【React】モダンな Javascript ~ Trello にあこがれて ~
 
								John
更新日:2024/10/29
こんにちは。
Reactについては
過去にサーバー/ミドルウェアの構築に加えて
バックエンドを Laravel(PHPフレームワーク) にて整備したりした経験がある john です。
そういった経緯がありながらも
コンパイル等の手間の兼ね合いにより
今回はフロントエンドの開発に重点を置いて着手してみました。


結論としては、こんな画面になります。

ディレクトリ構成については、こんな感じ。
| 1 | # tree -L 2 | 
| 1 2 3 4 5 6 7 8 9 10 11 12 | . ├── app │   ├── node_modules │   ├── package.json │   ├── package-lock.json │   ├── react-project          # Reactアプリケーション │   └── yarn.lock ├── Docker-compose.yml     # コンテナ構成(ポート等の指定) ├── Dockerfile                 # コンテナ構成(node.jsなどの構成要素指定) └── readme.md 3 directories, 7 files | 
では、アプリケーションの構成について。
メインで利用するライブラリ”react-beautiful-dnd” のリファレンス等を参考に複数のコンポーネントにて構成しました。
| 1 | tree -I "node_modules" | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | . ├── kanban_dnd │   ├── package.json │   ├── package-lock.json │   ├── public │   │   ├── favicon.ico │   │   ├── index.html │   │   ├── logo192.png │   │   ├── logo512.png │   │   ├── manifest.json │   │   └── robots.txt │   ├── README.md │   ├── src │   │   ├── App.css │   │   ├── App.js                                  # React最上位構成コンポーネント(要はindex.php) │   │   ├── components │   │   │   ├── header                                # 画面上部の構成コンポーネント    │   │   │   │   └── Header.jsx │   │   │   └── task                                # 画面中央以下の構成コンポーネント │   │   │       ├── button                            # カード内のボタン関連の構成コンポーネント │   │   │       │   ├── AddTaskCardButton.jsx │   │   │       │   └── DeleteTaskCardButton.jsx │   │   │       ├── input                             # カード内のインプット関連の構成コンポーネント │   │   │       │   └── TaskAddInput.jsx │   │   │       ├── TaskCard.jsx                     # カード内部の構成コンポーネント │   │   │       ├── TaskCards.jsx                    # カード外部の構成コンポーネント │   │   │       ├── TaskCardTitle.jsx                # カード上部のタイトル入力構成コンポーネント │   │   │       ├── Task.jsx                         # カード内のタスク入力の構成コンポーネント │   │   │       └── Tasks.jsx                        # カード内のタスク表示の構成コンポーネント │   │   ├── images │   │   │   └── MtFuji.jpg │   │   ├── index.css │   │   ├── index.js │   │   ├── logo.svg │   │   ├── reportWebVitals.js │   │   └── setupTests.js │   └── yarn.lock └── tree_i_node_module.txt 9 directories, 31 files | 
インフラ環境(コンテナ)については、シンプルにnode.jsが動く環境のみを準備
| 1 2 3 4 5 6 7 8 9 10 | version: '3' services:   app:     build: .     image: docker-nodejs     volumes:       - ./app:/app     ports:     - "3000:3000"     tty: true | 
| 1 2 3 | FROM node:lts-alpine3.19 WORKDIR /app CMD ["sh"] | 
アプリについては以下つらつらと
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import React from 'react' import Task from './Task' import { DragDropContext, Droppable } from "react-beautiful-dnd"; const reorder = (taskList, startIndex, endIndex) => {     const remove = taskList.splice(startIndex, 1);     taskList.splice(endIndex, 0, remove[0]); }; const Tasks = ({inputText, taskList, setTaskList}) => {     const handleDragEnd = (result) => {         reorder(taskList, result.source.index, result.destination.index);         setTaskList(taskList);     }   return (     <div>         <DragDropContext onDragEnd={handleDragEnd}>             <Droppable droppableId="droppable">                 {(provided) => (                     <div {...provided.droppableProps} ref={provided.innerRef}>                         {taskList.map((task, index) => (                             <div key={task.id}>                                 <Task task={task} index={index} taskList={taskList} setTaskList={setTaskList}/>                             </div>                         ))}                     {provided.placeholder}                     </div>       )}       </Droppable>       </DragDropContext>     </div>   ) } export default Tasks | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import React, {useState} from 'react' const TaskCardTitle = () => {     const [isClick, setIsClick] = useState(false);     const [inputCardTitle, setInputCardTitle] = useState("today task");     const handleClick = () => {         setIsClick(true);     }     const handleChange = (e) => {         setInputCardTitle(e.target.value);     }     const handleSubmit = (e) => {         e.preventDefault();         setIsClick(false);     }     const HandleBlur = () => {         setIsClick(false);     }   return (     <div onClick={handleClick} >         {isClick ? (<form onSubmit={handleSubmit} >             <input type="text" onChange={handleChange} onBlur={HandleBlur} value={inputCardTitle} maxLength="10"></input></form>) : <h3>{inputCardTitle}</h3>}     </div>   ) } export default TaskCardTitle | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | import React, { useState } from 'react' import TaskCard from './TaskCard' import AddTaskCardButton from './button/AddTaskCardButton' import { DragDropContext, Droppable } from 'react-beautiful-dnd'; const reorder = (taskCardsList, startIndex, endIndex) => {   // task sort   const remove = taskCardsList.splice(startIndex, 1);   taskCardsList.splice(endIndex, 0, remove[0]); }; const TaskCards = () => {   const [taskCardsList, setTaskCardsList] = useState([{     id: "0",     draggableId: "item0",   },]);   const handleDragEnd = (result) => {     reorder(taskCardsList, result.source.index, result.destination.index);     setTaskCardsList(taskCardsList);   };   return (     <DragDropContext onDragEnd={handleDragEnd}>       <Droppable droppableId="droppable" direction="horizontal">         {(provided) => (           <div className="taskCardsArea"{...provided.droppableProps} ref={provided.innerRef}>           {taskCardsList.map((taskCard, index) => (           <TaskCard key={taskCard.id} index={index} taskCardsList={taskCardsList} setTaskCardsList={setTaskCardsList} taskCard={taskCard}/>           ))}           {provided.placeholder}           <AddTaskCardButton taskCardsList={taskCardsList} setTaskCardsList={setTaskCardsList}/>         </div>         )}       </Droppable>     </DragDropContext>   ) } export default TaskCards | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import React, {useState} from 'react' import TaskCardTitle from './TaskCardTitle' import TaskCardDeleteButton from './button/TaskCardDeleteButton' import TaskAddInput from './input/TaskAddInput' import Tasks from './Tasks' import { Draggable} from 'react-beautiful-dnd'; const TaskCard = ({taskCardsList, setTaskCardsList, taskCard, index}) => {   const [inputText, setInputText] = useState("");   const [taskList, setTaskList] = useState([]);   return (     <Draggable draggableId={taskCard.id} index={index}>       {(provided) => (       <div className="taskCard" ref={provided.innerRef} {...provided.draggableProps}>       <div className="taskCardTitleAndTaskCardDeleteButtonArea" {...provided.dragHandleProps}>         <TaskCardTitle />         <TaskCardDeleteButton taskCardsList={taskCardsList} setTaskCardsList={setTaskCardsList} taskCard={taskCard}/>       </div>       <TaskAddInput inputText={inputText} setInputText={setInputText} taskList={taskList} setTaskList={setTaskList}/>       <Tasks inputText={inputText} taskList={taskList} setTaskList={setTaskList}/>     </div>       )}     </Draggable>   ) } export default TaskCard | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import React from 'react' import { Draggable } from "react-beautiful-dnd"; const Task = ({task, taskList, setTaskList, index}) => {     const handleDelete = (id) => {         // setTaskList         setTaskList(taskList.filter((task) => task.id !== id))     };   return (     <Draggable index={index} draggableId={task.draggableId}>         {(provided) => (             <div className="taskBox" key={task.id} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>                 <p className="taskText">                     {task.text}                 </p>                 <button className="taskTrashButton" onClick={() => handleDelete(task.id)}>                     <i className="fas fa-trash-alt"></i>                 </button>             </div>         )         }     </Draggable>   ) } export default Task | 
利用技術の通りにはなりますが
コンパイルの手間等の兼ね合いにより
Dockerにてコンテナ内にミドルウェア(node.js)等を構築、内部にてパッケージマネージャ(yarn)を利用したビルトインサーバー(開発サーバー)を起動する仕様としています。
※
この開発環境にて開発したReactソースを、そのままReact*Laravelに移管作戦って感じですね。
ただ上手に使いこなせれば、React*Laravel構成でもホットリロード的環境での開発が出来る気がしないでもないですが。。笑
今回はここまでです。
今後ともどうぞよしなに。
 
									