はじめに
タスク管理アプリの次のステップとして、React + Viteでメモ・ノートアプリ「メモ帳」を作りました。
CRUDに加えて「検索」と「Markdown記法のプレビュー表示」という、一段発展した要素を盛り込んでいます。
前作のタスク帳から意図的に変えた設計判断がいくつかあるので、
今回はその「なぜ変えたのか」を中心に書きます。
GitURL
技術スタック
| 項目 | 使用technology |
|---|---|
| フレームワーク | React 18 |
| ビルドツール | Vite |
| Markdown変換 | marked(軽量ライブラリ) |
| データ保存 | ブラウザの localStorage |
できること
- メモの作成・編集(自動保存)・削除
- タイトル・本文をキーワードで絞り込む検索
- 「編集」「プレビュー」タブの切り替えによるMarkdownプレビュー
- LocalStorageへの自動保存
画面は左にメモ一覧+検索欄、右に選択中メモの編集/プレビューという
2ペイン構成にしました。図書館の索引カードをイメージした落ち着いた配色にしています。
実装のポイント
Markdown変換は自作せず、ライブラリに任せた
import { marked } from "marked";
const html = marked.parse(content);
Markdownを自分でパースしようとすると、見出しやリストだけでなく、ネストしたリスト、コードブロック内の記号の扱い、テーブル記法など、考慮すべき例外パターンがどんどん増えていきます。ここは「よくある処理は、信頼できるライブラリに任せて、自分のアプリ固有の部分に集中する」という判断をし、軽量なMarkdownパーサーであるmarkedを採用しました。
ただしdangerouslySetInnerHTMLでHTMLをそのまま描画する都合上、
本来はDOMPurifyのような無害化ライブラリと組み合わせるのが安全です。
今回は「自分が書いた自分専用のメモ」しか表示しないため許容していますが、
複数人が使うアプリに発展させる場合は必須の対応だと考えています。
検索は includes() によるシンプルな一致検索
notes.filter(
(note) =>
note.title.toLowerCase().includes(query) ||
note.content.toLowerCase().includes(query)
);
あいまい検索や全文検索エンジンを使う方法もありますが、個人用メモが
数百件程度までの規模であれば、この最も基本的な方法で速度的にも実用上も十分です。
最初から複雑な仕組みを入れるとコードの見通しが悪くなるため、
「今の要件に対して十分な複雑さで実装する」ことを意識しました。
タスク帳から変えた設計判断
IDを Date.now() から crypto.randomUUID() に変更
前作のタスク帳では現在時刻をIDにしていましたが、今回は
ブラウザ標準のcrypto.randomUUID()(重複がほぼ起こらないID生成機能)に変更しました。
理由は、メモアプリでは将来的に「インポート機能」などで複数件を一度に作る可能性が
考えられるためです。同じ「IDをどう作るか」という課題でも、要件によって適切な実装が
変わることを、あえて前作と違う方法を選ぶことで実感できました。
useMemo で検索結果の再計算を最適化
const filteredNotes = useMemo(() => {
// 絞り込みと並び替え
}, [notes, searchQuery]);
notesかsearchQueryが変わった時だけ絞り込み処理をやり直すようにしています。
メモの件数が少ないうちは体感できる差はほぼありませんが、
「無駄な再計算がどこで起きうるか」を把握しておく練習として取り入れました。
つまずいたところ
-
dangerouslySetInnerHTMLという名前を初めて見たとき、なぜ「危険」と
名付けられているのか調べるところから始めました。他人の入力をそのままHTMLとして
描画するとXSS(悪意のあるスクリプトの埋め込み)のリスクがあるという背景を理解してから、自分のアプリでどこまで許容できるかを判断できるようになりました。 - 編集画面とプレビュー画面をタブで切り替える設計にするか、左右に並べて同時表示するかで迷いました。
最初はシンプルさを優先してタブ切り替えにしましたが、次に作るなら分割ビューにも挑戦したいです。
まとめ
検索とMarkdown対応という2つの新しい要素を加えたことで、「ライブラリを使うか自作するか」「同じ課題でも要件が変われば実装も変える」という、タスク帳だけでは得られなかった判断の経験ができました。次はフレームワークをVue.jsに変えて、家計簿アプリに挑戦しました。
そちらは別記事で紹介します。