メモ帳アプリの作成 27-29日目 2024/11/2-4(お金持ちまでの道のり)

英国紳士風のお金持ちアンドロイドの絵です。 プログラミング

本日はアプリ制作第二弾「メモ帳アプリ」を作ります。第三弾では「カウントダウンタイマー」、第四弾ではフォームアプリを作り、前回学んだSPAを実装します。

初めての人はお金持ちまでの道のり:0日目をまず読んでみてください!よろしくお願いします★

メモ帳アプリ

機能概要

メモの追加:ユーザーが入力したメモをリストに追加する。
メモの削除:追加したメモをリストから削除できるようにする。
メモの一覧表示:現在のメモがリスト形式で表示される。

今回のメモ帳アプリでは、Reactの機能を使って効率的にアプリを構築するためにNode.js command promptを使用します。

Node.js command promptを使う理由

今回のメモ帳アプリも前回のTodoリストアプリのようにHTMLだけでコードを書くこともできるのですが、今回はReactの機能を使って効率的にアプリを構築するためにNode.js command promptを使用します。

なぜReactを使うのか
  1. 効率的なコード構築
    • Reactのコンポーネントを使うと、アプリを小さな部品(コンポーネント)に分けて管理できます。これにより、コードの再利用が簡単になり、メンテナンスが楽になります。
  2. リアクティブなUIの構築
    • Reactでは、状態(state)を使って、データの変化に応じてUIを自動的に更新する仕組みがあるため、ユーザーの操作に対する反応が素早く行えます。
  3. 開発スキルの向上
    • Reactは業界で広く使われており、Reactの習得は他のプロジェクトやアプリ開発に役立ちます。今回のアプリではReactを練習し、スキル向上を目指します。
HTMLだけのアプリ vs Reactを使ったアプリ
★HTMLだけのTo-Doリストアプリ
  • シンプルに、HTML、CSS、JavaScriptを使って作られたアプリです。
  • ブラウザだけで動かせるので、特別な準備や設定は不要です。
  • 新しい機能を追加するときには、すべて手作業でコードを追加する必要があります。
  • たとえば「リストの項目を増やす」とか「見た目を変える」など、一つひとつの部分を自分でコードに書き込んでいきます。

★Reactを使ったメモ帳アプリ

Reactを使うと、効率よく、作りやすくなる仕組みがあります。

  1. コンポーネントがある
    • Reactでは、アプリを小さなパーツ(コンポーネント)に分けられます。
    • 例えば、「メモを書く部分」「メモを表示する部分」「削除ボタン」といったパーツごとに分けて作れます。
    • 各パーツを一度作れば、他の場所でも何回でも使えるので、作るのが楽になります。
  2. データが変わると自動で画面も変わる
    • Reactには「状態(state)」という仕組みがあって、メモやTo-Doリストのデータが変わると画面が自動で更新されます。
    • 例えば、メモを追加すると自動的にリストが更新され、手作業で再表示させる必要がありません。
  3. 作り直しが簡単
    • Reactは変更があっても、その部分だけ更新してくれるので、アプリを大きく作り直す必要がありません。
    • 作り直しが簡単だから、もっと大きなアプリや新しい機能を追加しやすくなります。

簡単なまとめ
  • HTMLだけのTo-Doリスト:全部自分で一から作っていく。少しずつ手作業で追加・変更していくイメージ。
  • Reactのメモ帳:パーツを組み合わせて作る。データが変わると自動で画面が変わり、大きなアプリを作るときにも便利。


Reactを使うと、たくさんの機能やパーツが簡単に追加でき、アプリがどんどん成長しても管理しやすくなります。

実装手順①

ステップ1:Node.js command promptでReactプロジェクトを作る

Node.js command promptを開く

  • パソコンで「Node.js command prompt」を探してクリックして開きます。これはReactのプロジェクトを作るための特別な窓です。

デスクトップなど、アプリを作りたい場所に移動する

  • cd Desktopと入力してEnterを押すと、デスクトップ上にアプリのフォルダが作られます(別の場所にしたい場合は、フォルダに合わせて変更できます)。

Reactプロジェクトを作成するコマンドを入力

  • 次のコマンドを入力して、新しいReactアプリのプロジェクトを作ります。アプリの名前は好きに決めていいですが、ここでは「my-memo-app」としてみましょう。
    ※npx create-react-app 〇〇(プロジェクト名)で名前を決める

これでプロジェクトが作成され、src(source)フォルダの中にApp.jsが自動で生成されます。
npx create-react-app my-memo-appで作られるのはApp.jsだけではなく、Reactアプリ全体のフォルダと基本ファイル一式です。App.jsはその中のメインファイルの一つです。

App.jsファイルとは、Reactプロジェクトのメインコンポーネントのファイルです。通常、Reactアプリを作成すると、このファイルがプロジェクトの中に自動で作成され、**アプリ全体の入り口(エントリーポイント)**として機能します。

ステップ2:Reactプロジェクトの準備

プロジェクトのフォルダに入る

  • さっき作ったフォルダに移動するため、次のように入力してEnterを押します。

アプリをスタート

  • Reactアプリをブラウザに表示するためのコマンドを入力します。

これでブラウザが自動的に開き、「Reactアプリ」が表示されます。この画面を見ながらコードを編集して、メモ帳アプリを作っていきます。

ステップ3:メモ帳アプリの中身を作る

VSCodeを開き、「App.js」ファイルを編集する

ここ注意!
VSCode(Code.exe)を開く→左上のファイル→フォルダーを開く→my-memo-appフォルダを1クリック→フォルダーの選択
※my-memo-appをダブルクリックしてはダメ!

フォルダーの選択をクリックすると下の画面が表示されます。

  • 左側のエクスプローラーのsrcをクリックすると「App.js」というファイルがあるのでダブルクリックする。このファイルでメモ帳アプリの中身を作ります。
  • まず、以下のように基本的なメモ帳アプリの構造を作成しましょう。
1. function App() { ... } の部分
  • App という名前の「アプリの本体」を作っています。この中に、メモ帳の画面を表示するためのいろいろな部品が入っています。
2. <div className="App"> ... </div> の部分
  • <div> は「部屋」のようなもので、ここに画面に表示するものをまとめて入れています。
  • className="App" は、部屋に「App」という名前をつけて、「ここがアプリ全体ですよ!」とわかりやすくしています。
    ※Appというfunctionと同じ名前にする必要はない ですが、特に小さなアプリの場合、名前を統一しておくとわかりやすくなります。
3. <h1>メモ帳アプリ</h1> の部分
  • <h1> はタイトルの役割をしています。画面に大きな文字で「メモ帳アプリ」と表示されます。
4. <input type="text" placeholder="メモを入力してください" /> の部分
  • <input> は入力ボックスです。ここにメモを書き込むことができます。
  • type="text" は、「この入力ボックスには文字を入力できますよ!」という指定です。ここで「text」としているので、文字や数字を自由に入力できるようになっています。
  • placeholder="メモを入力してください" は、入力ボックスにうっすらと表示される案内です。「ここにメモを書いてね!」とユーザーに教えてくれます。
5. <button>追加</button> の部分
  • <button> はボタンです。このボタンを押すことで、入力したメモがリストに追加される予定です(まだこの機能は追加していません)。
6. <ul> ... </ul> の部分
  • <ul> はメモのリストを表示する「リストの部屋」です。
  • {/* メモを表示するリストアイテムをここに追加 */} と書かれている部分は、「ここにメモをどんどん表示しますよ!」という意味です(コメントと呼ばれるもので、動作には影響しません)。
    ※ReactのJSX(JavaScript XML)では、通常のJavaScriptとは少し異なり、{/* ... */} の形式でコメントを書きます。
    Reactのコード全体でJSXを使うことができますが、通常は return の中にJSXを記述する のが一般的です。

全体の流れ

このアプリでは、まず入力ボックスにメモを書いて、「追加」ボタンを押すことで、メモをリストにどんどん追加していく、というイメージです。今はまだ基本の形だけですが、次に「メモを追加する機能」などを加えて、完成させていきます。

Visual Studio Codeの左上の三本線→ファイル→保存でコードが保存されるとReact Appの画面が自動的に以下の画像のようになり、現時点のアプリの状況を確認できます。以下が現時点でのアプリ画面です。入力後に追加ボタンを押しても何も起こりません。

なぜNode.jsとVSCodeの両方を使うのか?
  1. Node.jsで準備し、VSCodeで内容を作るため
    • Node.jsは、Reactアプリを始めるための環境(プロジェクトのフォルダやファイル)を作ってくれるツールです。
    • VSCodeは、作成したReactプロジェクトの中で、HTMLやJavaScriptを使ってアプリの見た目や動きを書き込むために使います。
  2. 実際のアプリ開発を簡単にするため
    • Node.jsで作成したReactプロジェクトは、VSCodeでコードを変更すると、ブラウザが自動的に更新されて、リアルタイムで結果を確認できます。これにより、アプリの開発がスムーズに行えます。

まとめ
  • Node.jsは、Reactプロジェクトを作成・準備してくれるツール
  • VSCodeは、そのプロジェクトのコードを書き換えたり編集するためのエディター

実装手順②

ステップ1: 状態管理用のuseStateを追加

Reactでは、コンポーネントの状態を管理するためにuseStateというフックを使います。このアプリでは、「メモのリスト」と「入力されたメモの内容」を状態として管理する必要があります。

  1. useStateをインポートします。
  2. メモのリストを保存するための状態(notes)と、メモの入力内容を保存するための状態(input)を追加します。
1. import React, { useState } from 'react';

これは「React」というプログラムの一部を使うための準備です。useStateというのは、「状態」といって、アプリの中で覚えておきたいものを管理するためのツールです。

2. function App() {

Appという名前の「アプリ本体」を作っています。この中でメモ帳アプリがどう動くかを決めていきます。

3. const [notes, setNotes] = useState([]);

const [状態, 更新関数] = useState([]);

const は、「この箱(変数)は名前を変えずに使い続けます」という決まりをつけて、変数(データを入れる箱)を作る言葉です。ここでnotesというメモのリスト(紙のメモのようなもの)を作っています。useState([]);で「最初は空っぽのメモのリストですよ」と設定しています。setNotesというのは、メモを追加したり変えたりするときに使います。

  • notes(状態):この変数は「メモのリスト」です。メモを追加するたびに、このリストの中にメモが増えていきます。
  • setNotes(更新関数):これは notes に新しいデータ(メモのリスト)を入れるためのボタンのような役割です。
    ※状態と更新関数はセットで名前を決める必要があります。例えば、状態を notes にした場合、更新関数も一緒に setNotes といった関連性がわかる名前にすると、コードの可読性が高くなります。
  • useState([])notes の中に、最初は [](空っぽのリスト) を入れています。何もメモがない状態を表しています。


つまり、**「最初は空のメモリスト(notes)を用意して、あとでメモが増えたら setNotes を使ってメモを追加します」**という意味です。

Reactの状態管理には const が推奨

Reactでは、useState で作成した状態の更新は setNotessetInput のように専用の更新関数を使います。これにより、状態の管理が明確になり、const が適しているのです。

4. const [input, setInput] = useState('');

ここでは、inputという名前で「入力された文字」を保存する場所を作っています。useState('');で「最初は何も書かれていない状態」にしています。setInputは、入力が変わったときにその内容を保存するために使います。
useState は、Reactで「メモリのように値を保存する場所」を作るための関数です。この関数を使うと、値を保存する「箱」と、その値を変えるための「スイッチ」が一緒に作られます。この「箱」の中の値が変わると、自動的に画面も更新されます。

  • input(状態):この変数は「ユーザーが入力したメモ内容」を一時的に入れておく箱です。
  • setInput(更新関数):これは input に新しいデータ(入力された文字)を入れるためのボタンのような役割です。inputが箱で、setInputがスイッチです。
    ※この構造で、入力内容が変わるたびに setInput を使って input を更新し、画面に新しい内容が反映されるようになります。
  • useState('')input の中に、最初は ''(空っぽの文字列) を入れています。まだ何も入力されていない状態を表しています。


つまり、**「最初は空の入力内容(input)を用意して、文字が入力されたら setInput を使ってその内容を保存します」**という意味です。

5. return ( ... )

returnの中で、アプリに表示する内容を決めています。ここに書かれたものが画面に出てきます。

6. <div className="App">

divは「アプリ全体をまとめる箱」みたいなものです。この中にアプリの内容が全部入っています。

7. <h1>メモ帳アプリ</h1>

<h1>は「アプリのタイトル」です。ここでは「メモ帳アプリ」というタイトルが表示されます。

8. <input type="text" ... />

<input>は「文字を入力する場所」です。ここでユーザーがメモを入力できます。

  • value={input}は、今書かれている内容を表示します。
  • onChange は、「入力ボックスに何か文字を入力するたびに、ある動きをさせたい」というときに使う特別な設定です。
  • {(e) => ... }{} は「この中はJavaScriptの特別な計算や動きがありますよ」という意味です。onChange に何をするかを {} の中に書きます。
  • (e) => ... という書き方は「アロー関数」といって、Reactでよく使います。この場合、(e) というのは「イベント」のことです。文字を入力すると、その「イベント」が起こり、e というものの中に「どんな文字が入力されたか」が入っています。
    =onChange={function(e) { setInput(e.target.value); }}
  • e.target.value は「今入力された文字」を指します。
  • setInput は「input に入れる内容を更新する」ための特別な関数です。setInput(e.target.value) によって、input という状態に「今入力された文字」が保存されます。
  • onChange={(e) => setInput(e.target.value)}は、何か文字が書き込まれるたびにinputに保存します。
  • placeholder="メモを入力してください"は、何も書かれていないときに「メモを入力してください」という文字が薄く表示されます。
9. <button onClick={addNote}>追加</button>
  • 「追加」ボタンです。onClick={addNote}は、ボタンを押したときにaddNoteという機能(メモを追加する機能)を動かす設定です。このaddNoteはまだ書いていないので、次に書く必要があります。
  • {} は「この中はJavaScriptですよ」という意味です。JSX(HTMLに似た構文)の中にJavaScriptを埋め込むために使います。
  • {} の中に書くと、JavaScriptの変数や関数を使うことができます。addNote は関数なので、{} の中に書くことで「このボタンがクリックされたら addNote 関数を実行する」という設定ができます。
    ※ここではまだaddNote 関数が定義されていないため、クリックしても何も起こりません。
10. <ul> ... </ul>

<ul>は「メモのリスト(一覧)」です。

notes.map((note, index) => (...)

  • notes は今までに追加したメモが入っているリストです(例えば、「買い物する」、「宿題をする」などが入っています)。
  • map は、配列(ここでは notes 配列)の各要素に対して処理を行い、その結果を新しい配列として返すための関数です。(※mapJavaScriptの組み込みメソッド
    • 配列の各要素に関数を適用notes 配列の各要素(ここでは各 note)に対して、指定した関数((note, index) => (<li key={index}>{note}</li>))を順番に適用します。
    • 新しい配列を返すmap 関数が生成した <li> 要素のリストを新しい配列として返し、React はその配列をレンダリングします。
    • インデックスを使ってユニークなキーを指定:各要素に key 属性を付けるために index を利用しています。React がレンダリングや再描画を効率的に行うために、key 属性は必須です。
  • (note, index) => (...) の部分は、リストの中の「メモ」(note)と、その順番(index)を1つずつ取り出して、「このメモを画面に表示するよ!」という指示を書いています。
map 関数の主な機能
  • 配列の各要素に関数を適用:配列のすべての要素に対して指定した関数を順に適用します。
  • 新しい配列を返す:元の配列は変更せず、新しい配列を返します(元の配列はそのまま保持されます)。
  • コールバック関数の引数
    • 要素(element):現在の配列の要素。
    • インデックス(index):現在の要素が配列の何番目かを示す番号。
    • 配列(array):元の配列全体。
  • 便利なデータ変換:配列の各要素に対して何らかの処理をして、新しい形式のデータに変換するのに役立ちます。


map は、新しい配列を作るときに効率的に使える組み込みメソッドです。


<li key={index}>{note}</li>

  • <li> は「リストのアイテム」という意味で、メモを1つずつリスト形式で表示します。
  • key={index}
    • これはReactが「どのメモがどれか」を覚えるための目印です。たとえば、1番目のメモと2番目のメモがわかるように、1つ1つに番号をつけています(indexがその番号です)。
  • {note}
    • これは「メモの内容」をそのまま画面に表示する部分です。たとえば、「買い物する」というメモが入っていれば、ここに「買い物する」が表示されます。
  • {notes.map((note, index) => ( ... ))}は、notesに入っているメモを1つずつ取り出して、画面に表示します。
  • <li key={index}>{note}</li>は、メモを1つずつ<li>の中に入れて表示するところです。
11. export default App;

このアプリを他の場所でも使えるようにしています。このメモ帳アプリで export default App; は必須 です。

  • このメモ帳アプリは、Reactアプリのエントリーポイント(最初に表示する部分)として App コンポーネントを使っています。
  • 例えば、index.js ファイルで App コンポーネントを読み込み、画面に表示する必要があります。
  • export default App; がないと、index.js など他のファイルから App をインポートして使用できなくなります。
  • Reactアプリでは通常、index.js からメインのコンポーネント(この場合は App)を読み込んで、アプリ全体をブラウザに表示します。
  • export default App; を付けておかないと、index.js から App をインポートできず、アプリを正常に表示できなくなります。

export default App;function App(コンポーネント)を指します

className="App" はCSSクラス名で、スタイルを適用するためだけに使われているものです。

ステップ2:addNote関数の追加

このコードには addNote 関数がまだ定義されていないので、ボタンをクリックしたときに実行する addNote 関数を追加する必要があります。このままのコードだと以下のようなエラーが出ます。

addNote は、ユーザーが入力したメモをメモリストに追加するための関数です。この関数を追加することで、ボタンをクリックしたときに入力内容がリストに追加されるようになります。

以下のように addNote 関数を追加して、ボタンをクリックしたときにメモがリストに追加されるようにします。

  • const addNote = () => { ... };
    • addNote 関数は、メモをリストに追加するための機能を持っています。
  • if (input.trim()) { ... }
    • trim() は、文字列の前後にある空白を取り除くための関数です。
      input = " 勉強する ";
      input.trim(); // "勉強する" (空白が取り除かれる)

      ※trimはJavaScript の組み込み関数
    • 「もし inputtrim空白以外の内容を持っているなら」、中の処理を実行します。
    • input.trim() は、input の前後の空白を取り除いた文字列を返します。
    • if (input.trim()) の部分では、空白以外の文字が入力されている場合のみ条件が true になります。空白だけなら条件は false になります。
  • setNotes([...notes, input]);
    • notes の現在のリストに input の内容を追加し、それを setNotes で新しいリストとして保存する」
    • 現在の notes リストに、新しいメモ(input に入力された内容)を追加します。
    • ...notes は、現在のメモのリストをすべて展開し、新しいメモ(input)を追加して更新する書き方です。
    • もし notes["メモ1", "メモ2"]input"メモ3" なら、[...notes, input]["メモ1", "メモ2", "メモ3"] になります。
  • setInput('');
    • メモを追加した後、input の内容を空の文字列にして、入力欄をクリアします。

これで以下の画像のようにメモをリスト化できます。

実装手順③

ステップ1:メモを削除する機能の追加

deleteNote 関数:

  • deleteNote という関数を新しく作りました。この関数は、メモを削除するためのものです。
  • index は削除するメモの位置を表します(何番目のメモかを示す番号)。
  • notes.filter((note, i) => i !== index) は、リストの中で index と一致しないメモだけを新しいリスト newNotes に残す、という意味です。アロー関数を使わない場合は以下の通りです。
    const newNotes = notes.filter(function(note, i) {return i !== index;});


filter メソッド:

  • filter は、条件に合う要素だけを残すためのJavaScriptの関数です。この場合、i !== indexiindex と違う)という条件でフィルタをかけています。つまり、index のメモだけを除外して新しいリストを作ります。
  • filter はJavaScriptの組み込み関数です。


削除ボタン:

  • notes 配列の各要素 note とその位置 index を使って、新しい配列を生成します。」
  • notes.map((note, index) => ... の中で、各メモに 削除 ボタンを追加しています。
  • onClick={() => deleteNote(index)} は、削除ボタンがクリックされたときに deleteNote 関数が呼ばれ、メモが削除されるようにしています。
  • {note}:Reactでは {} の中に変数を入れると、その値が JSX 内で表示されます。ここでは、各メモの内容を画面に表示するために {note} と書いています。

=の使い方について

このコードでメモを削除できるようになります。

ステップ2:編集ボタンの追加

メモの編集機能を実現する
  1. const [editingIndex, setEditingIndex] = useState(null);
    • useState を使って editingIndex(編集中のメモの番号)を初期値 null で設定し、更新用の関数 setEditingIndex を定義します。」
    • editingIndex(箱):今、編集中のメモがどのインデックス(番号)かを保存するための変数です。
    • setEditingIndex(スイッチ):editingIndex の値を更新するための関数です。
    • useState(null):初期値として null を指定しているので、最初はどのメモも編集していない状態です。
      ※たとえば、2番目のメモが編集されると editingIndex は 1 になります。
      ※useState は、Reactで「メモリのように値を保存する場所」を作るための関数です。この関数を使うと、値を保存する「箱」と、その値を変えるための「スイッチ」が一緒に作られます。この「箱」の中の値が変わると、自動的に画面も更新されます。
  2. const [editingInput, setEditingInput] = useState('');
    • editingInput(箱):編集中のメモの内容を保存するための変数です。実際に編集された内容をこの変数に一時的に保存します。
    • setEditingInput(スイッチ):editingInput の値を更新するための関数です。
    • useState(''):編集欄が最初は空なので、''(空の文字列)で初期化しています。


使い方の例

  • 編集ボタンをクリックすると、editingIndex に編集するメモの番号が入り、editingInput にそのメモの内容が入ります。
  • 編集内容が保存されると、editingIndex は再び null に戻り、editingInput は空にリセットされます。


この2つの状態を使うことで、どのメモが編集中で、編集欄にどの内容が入っているかを管理できるようになります。

編集ボタンがクリックされたときの関数
  1. const startEditing = (index) => { ... }
    • startEditing という関数を作っています。この関数は、どのメモを編集するのかを設定するためのものです。
    • (index) は、クリックされたメモが「何番目」なのかを表します。この番号を index として関数に渡しています。
  2. setEditingIndex(index);
    • setEditingIndex は、「編集中のメモの番号(インデックス)」を保存するための特別な関数です。
    • 例えば、2番目のメモを編集しようとすると、index が 2 になり、setEditingIndex(2); が実行されます。
    • これによって、Reactは「今2番目のメモを編集中だ」と認識します。
  3. setEditingInput(notes[index]);
    • setEditingInput は、「編集するメモの内容」を入力欄に表示するための関数です。
    • notes[index] は、notes リストの中で、クリックされたメモの内容を取り出します。
    • 例えば、3番目のメモが「宿題をする」なら、notes[2] で「宿題をする」を取得し、setEditingInput("宿題をする"); として入力欄にその内容を表示します。


具体的な流れ

たとえば、3番目のメモ「宿題をする」を編集したい場合、次のように動きます。

  1. 「編集」ボタンをクリック すると、startEditing 関数が呼ばれ、index が 2(3番目のメモ)として渡されます。
  2. setEditingIndex(2); が実行され、Reactは「3番目のメモが編集中である」と認識します。
  3. setEditingInput(notes[2]); で、メモ「宿題をする」の内容が入力欄に表示されます。


まとめ

  • startEditing は、どのメモを編集するのかを決めて、内容を入力欄に表示するための関数です。
  • 編集するメモの番号index)と、メモの内容notes[index])をセットしています。
  • これにより、編集するメモがクリックされたときに内容が入力欄に表示され、編集ができるようになります。
編集内容を保存する関数
  1. const saveEdit = () => { ... }
    • saveEdit という名前の関数を定義しています。この関数は、「編集した内容を保存する」役割を持ちます。
  2. const newNotes = [...notes];
    • notes は、現在のメモのリストです。この行で notes の内容をコピーした新しいリスト newNotes を作成しています。
    • [...notes] というスプレッド構文を使うことで、現在のメモを変更せず、新しい配列 newNotes を作ります。こうすることで、Reactの状態管理が正しく働きます。
スプレッド構文の説明とその役割

[...notes] というスプレッド構文を使うと、notes という配列の「中身」をそのままコピーして新しい配列 newNotes を作ります。

これをする理由は、「元の notes 配列をそのまま残しつつ、内容が同じ新しい配列を作るため」です。Reactでは、このように元の状態(配列)を直接変更せず、新しい状態を作ることで、変更を正しく認識し、画面が更新されます。

もし notes[1, 2, 3] なら、[...notes][1, 2, 3] のコピーを新しく作るという意味になります。

  1. newNotes[editingIndex] = editingInput;
    • editingIndex は今編集しているメモの番号です。
    • editingInput は編集された内容です。
    • ここで、newNoteseditingIndex 番目に editingInput(編集内容)を代入して、newNotes の中身を新しい内容に更新します。
    • たとえば、2番目のメモが編集されているならば、その内容が newNotes[2] に新しいテキストとして保存されます。
  2. setNotes(newNotes);
    • 更新した newNotessetNotes 関数で保存し、Reactに「メモのリストが変更された」と認識させます。
    • これにより、画面に表示されるメモが新しい内容で更新されます。
  3. setEditingIndex(null);
    • 編集が完了したので、editingIndexnull にします。
    • これにより、どのメモも編集中ではないと判断され、編集モードが解除されます。
  4. setEditingInput('');
    • 編集用の入力欄(editingInput)の内容を空の文字列 '' にします。
    • これにより、編集が終わった後に入力欄がクリアされ、次の編集に備えることができます。


具体例

たとえば、3番目のメモ「宿題をする」を「買い物に行く」に変更するとしましょう。

  1. newNotes に現在のメモリストをコピー。
  2. newNotes[2] = "買い物に行く" で3番目のメモを更新。
  3. setNotes(newNotes); で更新されたメモを保存。
  4. 編集モードを解除し、入力欄をクリアして完了。


まとめ

この関数は、「編集したメモを新しい内容で保存し、編集を終了する」ためのもので、Reactがメモの変更を認識し画面に反映する仕組みになっています。

編集中であれば編集用の入力欄と保存ボタンを表示する
三項演算子

条件 ? 真の場合の処理 : 偽の場合の処理
※条件が成り立てば真の処理をし、成り立たない場合は偽の処理をします。
※三項演算子は、条件が真か偽かで実行する処理を切り替えるための簡潔な書き方です。これにより、1行で条件分岐を簡潔に書くことができるので、UIの表示を条件によって切り替える場合に便利です。

  • 条件
    editingIndex === index
  • 真の場合の処理
    ( <> <input type="text" value={editingInput} onChange={(e) => setEditingInput(e.target.value)} /> <button onClick={saveEdit}>保存</button> </> )
  • 偽の場合の処理
    ( <> {note} <button onClick={() => startEditing(index)}>編集</button> <button onClick={() => deleteNote(index)}>削除</button> </> )
  1. {editingIndex === index ? ... : ... }:
    • これは三項演算子と呼ばれる構文で、「もし editingIndexindex と等しいなら最初の内容を表示、そうでない場合は2番目の内容を表示」という条件を設定しています。
    • editingIndex === index は、「現在表示しているメモが編集中かどうか」を確認するための条件です。
    • もし editingIndexindex と等しい場合、そのメモは現在編集中だと判断します。
  2. <input><button>(真の場合の表示):
    • <></> はフラグメントで、複数の要素を一つにまとめています。
    • <input type="text" value={editingInput} ... />:
      • これは、メモの内容を編集するための入力欄です。
      • value={editingInput} で現在の編集内容を表示し、onChange イベントで編集内容が変更されるたびに editingInput を更新します。
    • <button onClick={saveEdit}>保存</button>:
      • 「保存」ボタンで、クリックすると saveEdit 関数が実行され、編集内容が保存されます。
  3. : ( ... ) の部分(偽の場合の表示):
    • この部分は、「編集中でない場合の表示内容」を書くための場所です。
    • 例えば、通常のメモ表示や「編集」「削除」ボタンなどがここに入ります。


まとめ

このコードにより、メモが編集中であれば「編集用の入力欄と保存ボタン」が表示され、編集中でない場合は通常のメモ表示に切り替わる仕組みになります。

このコードで編集ができるようになります。編集ボタンをクリックすると二枚目の画像のようになり、編集した新しいメモを保存できます。

ステップ3:日時情報を追加する

現在のコードでは、メモの内容を直接配列に追加していますが、メモの内容だけでなく日時も一緒に保存するために、オブジェクトとして保存します。

  • const newNote は、新しいメモを作るための「箱」を作っていると考えてください。この箱の中に「内容」と「日時」を入れます。newNote はオブジェクトとして定義されています。
  • content: input は、メモの「内容」を意味しています。ここでは input(入力されたメモの内容)をそのまま入れています。content はキー、input はその値です。
  • timestamp: new Date().toLocaleString() は、メモが作られた「日時」を表しています。new Date() は「今の日時」を取得し、toLocaleString() で読みやすい形式に変えています(例えば、「2024/11/4 14:30」のような形)。timestamp はキーで、「日時」を示します。値として new Date().toLocaleString() が使われています。new Date() はJavaScriptの組み込み関数で、現在の日時を取得し、toLocaleString() は、Date オブジェクトの組み込みメソッドで、日時をわかりやすい形式に変換します(例えば、「2024/11/4 14:30」など)。
    ※キーと値の組み合わせをひとまとめにしたものを オブジェクト と呼びます。オブジェクトは { } で囲まれ、内部に プロパティ と呼ばれる「キーと値のペア」を持っています。


まとめ

このコードは、「メモの内容」と「いつ作ったかの日時」をセットで保存するためのものです。

notes.map の中で、note という変数がオブジェクトになったため、note.contentnote.timestamp を使って内容と日時をそれぞれ表示します。

  1. <div>{note.content}</div>
    • <div> は、HTMLのタグの一つで、内容を囲むための「箱」みたいなものです。
    • {note.content} の部分は、JavaScriptのコードをHTMLの中で使うために { } で囲まれています。
    • note.content は、そのメモの「内容」(テキスト)を指しています。これは、前に定義したオブジェクト newNotecontent プロパティに対応しています。
    • つまり、note.content の部分にはメモのテキストが入っており、それを <div> タグで囲んで表示します。
  2. <small>{note.timestamp}</small>
    • <small> は、HTMLのタグで、文字を小さく表示するために使われます。
    • {note.timestamp} は、そのメモの「作成日時」を指しています。これも newNote の中の timestamp プロパティに対応しています。
    • note.timestamp には、そのメモが作成された日時が入っており、これを <small> タグで囲んで、小さな文字として表示します。


まとめ

  • <div>{note.content}</div> はメモの内容(テキスト)を表示する部分。
  • <small>{note.timestamp}</small> は作成日時を小さな文字で表示する部分です。


このコードによって、各メモのテキストと作成日時が、それぞれのHTML要素で画面に表示されます。

{note} だけではなく <div>{note.content}</div> としている理由は、メモに関する情報が「内容」と「日時」に分かれたためです。

元のコードでは、note はただの文字列(例えば "海に行く" など)で、メモの内容だけを表していました。しかし、新しいコードでは note はオブジェクトであり、contenttimestamp という2つの情報(プロパティ)を持っています。

具体的な変更内容
  • 元のコード: {note} は文字列を直接表示していた。
  • 新しいコード: note{ content: "海に行く", timestamp: "2024-11-04 14:00" } のようなオブジェクトになり、その中の content プロパティを {note.content} で表示し、timestamp プロパティを {note.timestamp} で表示するようになった。
まとめ

<div>{note.content}</div> にしないと、note がオブジェクトであるため、直接 {note} と書くと、[object Object] という表示になってしまいます。note.content とすることで、オブジェクトの中の「内容」部分だけを取り出して表示することができるわけです。

これでメモを追加した日時が表示されるようになります。

このメモ帳アプリは SPA(シングルページアプリケーション) の仕組みを含んでいます。

なぜSPAといえるのか?
  1. ページのリロードがない
    • メモを追加したり、削除したり、編集したりしても、ページ全体をリロードせずに内容が更新されます。これはReactが提供する仕組みで、アプリの特定の部分だけを更新します。
  2. Reactの状態管理
    • Reactの useState フックを使って、メモの内容を管理しており、ユーザーが操作した内容がすぐに画面に反映されます。
    • これはJavaScriptがバックグラウンドで動いて、必要な部分のみを更新しているからです。
  3. 単一のHTMLファイルで動作
    • 通常、SPAでは1つのHTMLファイル(index.html など)で全ての表示内容を管理します。このアプリも、 index.html の中でReactが動作し、ページの一部だけを更新しているので、結果としてSPAの形をとっています。

まとめ

このメモ帳アプリは、ページ全体のリロードがなく、ユーザーの操作に応じて特定の部分だけが更新される仕組みが含まれているため、SPAといえます。

このコードをwordpressのカスタムHTMLに貼り付けるには以下の①を②に変える必要があります。

①の import React, { useState } from 'react'; というコードは、Reactをモジュールとしてインポートする構文です。これは通常、JavaScriptのモジュールをサポートしている環境(例えば、Node.jsやモジュールバンドラーを使用している環境)で使いますが、WordPressのカスタムHTMLブロックではそのまま使えません。

②のように <script> タグを使ってReactとReactDOMを直接読み込むことで、WordPress環境でもReactの機能を使えるようにしています。この方法では、Reactがグローバルオブジェクトとして React として提供され、{ useState } のように直接参照して使用できます。

  • <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>:React本体を読み込みます。
  • <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>:ReactDOMを読み込みます。
  • <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>:Babelを使用してブラウザ上でJSXを解釈できるようにしています。


これにより、Reactの useState フックなどの機能をWordPress上でも使用できるようになります。

あとこのメモデータをローカルストレージに保存するには以下のコードを修正する必要があります。

追加されたコード

修正されたコード
addNote、deleteNote、saveEdit関数内でsetNotesを削除し、代わりにsaveNotesToLocalStorageを使用してローカルストレージに保存。

addNote関数(修正後)

saveNotesToLocalStorageの役割

  1. ローカルストレージにデータを保存
    javascript
    コードをコピーする
    localStorage.setItem('notes', JSON.stringify(newNotes));
    localStorage.setItemを使って、メモデータをローカルストレージに保存します。


notesというキーで保存し、次回ページが読み込まれたときにこのデータを取り出せるようにします。
JSON.stringifyを使って、配列(またはオブジェクト)形式のデータを文字列に変換しています。ローカルストレージには文字列しか保存できないため、この変換が必要です。

  1. Reactの状態を更新
    javascript
    コードをコピーする
    setNotes(newNotes);
    setNotesを使って、Reactの状態notesも更新します。


状態が更新されることで、Reactが再レンダリングを行い、最新のメモ内容が画面に表示されます。
なぜsaveNotesToLocalStorageを使うのか?
メモデータの追加、削除、編集があるたびにこのsaveNotesToLocalStorage関数を呼ぶことで、以下の2つの処理を同時に行えます。

ローカルストレージへのデータ保存(ページ更新後もデータが保持される)
Reactの状態更新(画面に即時反映される)
これにより、メモの内容が常にローカルストレージとReactの状態の両方に反映されるようになり、ページを更新してもデータが消えない仕様が実現されています。

deleteNote関数(修正後)

saveEdit関数(修正後)

変更された部分

以下が修正したコード全体です。

本日の感想

ものすごく苦労して作った割に出来上がったアプリはものすごいシンプル。基礎を学ぶためには仕方がないことだけどつまらないアプリの作成はしんどいです。入力したメモをローカルストレージに保存して、画面を更新してもメモが消えないようにするコード変更も地味に難しい。これだけのことになんでこんな大幅な変更が必要なのかと思ってしまいます。

録画していたテレビ番組を見たら、写真データをAIに読み込ませて指示を出すと、その指示通りに写真に写った人物が動き出すというアプリが紹介されていました。これからどんどん今までになかったものがAIによって作られていくと考えると、メモ帳アプリくらいしか作れない自分のレベルに焦りを感じます。

ここに初めて来た人はゼヒ0日目を読んでください。プログラミング知識0のおじさんがお金持ちになるまでのプランと熱い思いが載っています。私と一緒に運要素を排除したゴリゴリの脳筋プレイでお金持ちを目指しませんか?

0日目の記事はこちら
お金持ちまでの道のり:0日目

コードの読み方が全く分からないという方は以下の記事がオススメです。コードの読み方の説明が一番詳しく書かれてある記事です。

ToDoリストアプリの作成 12-15日目 2024/10/18-21(お金持ちまでの道のり)

コメント

タイトルとURLをコピーしました