はじめに
みなさん、こんにちは torihaziです
今日は午前中にはauthorization付きのリクエストを送ってそれを元に
diaryの一覧ページを表示させることを完成させ、午後からは
crudのページ関連を作り込んでいこうと思います。
技術選定
[frontend]
- Nextjs(pages router) => App Routerの理解に苦しんだため
- MUI => 調べたランキングでtopだったため
- react-hook-form => フォーム管理と言ったらこれでは?
- zod => 少しだけ使い慣れてるから
- axios => httpリクエスト送るのはこれでは?
- recoil => 状態管理、useStateのような扱いで使いやすい
- js-cookie => cookieを扱う上で使用。軽く、更新も頻繁にされている
[backend]
- Rails 7 => 使い慣れているため
- devise => 定番だから
- devise-jwt => devise-token-authが古いらしいのでこっち
- letter_opener_web => 開発環境で確認メール確認用
[github]
まずはdiaryページの認証をパスする
認証情報はセットできるようになったので、今度はdiaryのindex actionにapiリクエストできるようにする。
ということでuseSWRを入れる。
npm i swr
import useSWR from "swr";
import { api } from "./axios";
//
// index
//
const indexFetcherAndKey = () => {
const key: string = "/api/v1/diaries";
const fetcher = (key: string) => api.get(key);
return { key, fetcher };
};
export const useDiaries = () => {
const { key, fetcher } = indexFetcherAndKey();
const { data, error, isLoading } = useSWR(key, fetcher);
return {
data,
error,
isLoading,
};
};
こんなんで行けんのでは?
行けてそう。
デザインがとにかく大変。
慣れない。
最低限はやらないと、見た目がしょぼいとモチベが上がらない。
気力が切れかけている。
あれかな、一気にやろうとするから「あー」ってなるのかな
小さなところのここだけ!ていう形でちょくちょく進めていこうかな。
デザインのイメージ。

開発日記のところは Container使おうかな、
使い方わっっかりづらい。
Material UI Box、Container、Typographyの違い
boxでいいや。使いづらそう。
だー。進捗悪い。
やる気出ない。
少しづつtypography のやり方がわかってきたのかも。
variantもcomponentも指定できるんだ。
componentはどのhtmlタグとしてそいつを扱うか
variantはなんだろう、そのテキスト全体のスタイルみたいなやつ
日付のフォーマットを扱う必要が出てきた。ライブラリはdayjsに決めた
dayjs/docs/ja/README-ja.md at dev · iamkun/dayjs · GitHub
なんか伸びてきているらしいので。
現状。

やっぱりMUIはカスタマイズ性が高いと思う。
多分自分が使いこなせていないだけだと思う。
やたらsxとか使っていたけど、使わずとも十分可能なのかもしれない。
次のやつで描き直すか、それとも一通りできたらリファクタしよ。
まずは全部の機能を一通り。
まずはログインユーザが持ってる日記の一覧を取得することはできた。
試しにログインユーザ変えてやってみようかな。
これでできてなかったとしたら。。一旦進もう。笑

行けてた。嬉しい😆
検索とかもできないけど、次の詳細画面にいこう。
まずはbackendの構築。
といっても
def show
render json: @diary, status: :ok
end
private
def set_diary
@diary = Diary.find(params[:id])
end
こんな感じで行けんのでは?
あとはbefore_actionのonlyの書き方。
【Rails】 before_actionの使い方とオプションについて | Pikawaka
なかなか覚えられない、というかやっぱりシンボルの書き方いまだに慣れない。
ガチャガチャやって、indexとshowの画面はできた。
index

show

サンプルの文章量もっと増やそう。
というか今思ったけど、frontはすごいガチャガチャ書いているけど、
backendこんだけだ。
class Api::V1::DiariesController < ApplicationController
before_action :authenticate_api_v1_user!
before_action :set_diary, only: [:show]
def index
@diaries = current_api_v1_user.diaries.all
render json: @diaries, status: :ok
end
def show
render json: @diary, status: :ok
end
private
def set_diary
@diary = Diary.find(params[:id])
end
end
まだ抜け漏れあるかもしれない。
シリアライザ使ったりするともっと
かっこいいというかなんとかなると思うけど。
なんか少し拍子抜けという感じ。
まぁ認証がとにかく難しかったからあれなのかな。
とりあえず作り切ることをゴールとしよう。
新規作成
ここからこの日記の本題、日記作成の機能を着手する。
日記作成について使用するライブラリは Editorjs。
WYSIWYGなものはいくつかあるが、これを選んだのは理由がある。
実務で使用しているからというのとそれなのに理解が浅いからそれを克服するために。
以前書いたものを参考に思い出しながら。
Nextjs で Editorjsを使ってみた part2 - Torihaji's Growth Diary
まずいれる。
前回は使わなかったdynamic import使ってみよう。
Next.jsでのDynamic Importの使い所 - Wiz テックブログ
Advanced Features: 動的インポート | Next.js
Integate Editor Js in React Js and NextJs with App Router
ということで3つ目のものを参考に。
npm i @editorjs/code @editorjs/embed @editorjs/header @editorjs/inline-code @editorjs/list @editorjs/nested-list @editorjs/paragraph @editorjs/quote
画像はまだいいや。
こんな感じに設定は落ち着いた。長い。
import Paragraph from "@editorjs/paragraph";
import Header from "@editorjs/header";
import Quote from "@editorjs/quote";
import Warning from "@editorjs/warning";
import Delimiter from "@editorjs/delimiter";
// import List from "@editorjs/list";
import NestedList from "@editorjs/nested-list";
import Embed from "@editorjs/embed";
import CodeTool from "@editorjs/code";
import InlineCode from "@editorjs/inline-code";
// npm i @editorjs/code @editorjs/embed @editorjs/header @editorjs/inline-code @editorjs/list @editorjs/nested-list @editorjs/paragraph @editorjs/quote
export const EDITOR_CONFIG = {
paragraph: {
class: Paragraph,
inlineToolbar: true,
},
header: {
class: Header,
shortcut: "CMD+SHIFT+H",
config: {
placeholder: "Enter a header",
levels: [2, 3, 4],
defaultLevel: 3,
},
},
quote: {
class: Quote,
inlineToolbar: true,
shortcut: "CMD+SHIFT+Q",
config: {
quotePlaceholder: "Enter a quote",
captionPlaceholder: "Quote's author",
},
},
warning: {
class: Warning,
inlineToolbar: true,
shortcut: "CMD+SHIFT+W",
config: {
titlePlaceholder: "Title",
messagePlaceholder: "Message",
},
},
delimiter: Delimiter,
list: {
class: NestedList,
inlineToolbar: true,
config: {
defaultStyle: "unordered",
},
},
embed: {
class: Embed,
inlineToolbar: true,
},
code: CodeTool,
inlineCode: {
class: InlineCode,
shortcut: "CMD+SHIFT+M",
},
};
色々いじってみて、warningとparagraphとembedが入んなかったので外した。

コードはこんな感じ。
import { DiaryTemplate } from "@/components/template/DiaryTemplate";
import { OutputData } from "@editorjs/editorjs";
import { Button } from "@mui/material";
import dynamic from "next/dynamic";
import { useState } from "react";
const Editor = dynamic(() => import("@/features/editorjs/editor-js"), {
ssr: false,
});
const DiaryNew = () => {
const [data, setData] = useState<OutputData | null>(null);
return (
<DiaryTemplate>
{/* ここに<form id="diary-form">タイトルinput</form>みたいなものを入れ込む */}
{/* で buttonにsubmit設定して、form="diary-form"みたいにして連携させる*/}
{/* あとはRHFのonsubmitでeditorのdataをsubmitのdataにくっつけてbackend送信 */}
<Editor value={data} onChange={setData} holder="editorjs" />
<Button onClick={() => console.log(data)}>保存する</Button>
</DiaryTemplate>
);
};
export default DiaryNew;
import EditorJS, { OutputData } from "@editorjs/editorjs";
import { useEffect, useRef } from "react";
import { EDITOR_CONFIG } from "./editor-config";
import { Box } from "@mui/material";
const Editor = ({
value,
onChange,
holder,
}: {
value: OutputData | null;
onChange: (data: OutputData) => void;
holder: string;
}) => {
const editorRef = useRef<EditorJS | null>(null);
useEffect(() => {
if (!editorRef.current) {
const editor = new EditorJS({
holder: holder || "editorjs",
placeholder: "入力してください",
tools: EDITOR_CONFIG,
// data: value
async onChange(api, event) {
const data = await api.saver.save();
onChange(data);
},
});
editorRef.current = editor;
}
return () => {
if (editorRef.current && editorRef.current.destroy) {
editorRef.current.destroy();
editorRef.current = null;
}
};
}, []);
return (
<Box
id={holder}
p={1}
pr={0}
minHeight="300px"
width="100%"
border="1px solid #E4E7EB"
borderRadius="10px"
></Box>
);
};
export default Editor;
ちなみに保存ボタンを押すと今はconsole.logに流れるだけだけど
こんな感じでdataは流れる。

一時保存機能とかはないけど、まぁいずれってことでいいでしょ。
あとはこれを保存して詳細ページで表示させるときにどうやって描画させるかだ。
保存されるのはjsonのデータだし。更新の時は
保存したjsonのデータをparseして読ませれば行けることは実務の経験上知ってるからなんとかなりそうだけど。
終わりに
でもまぁよく頑張った。一旦。
今日は終わりにしよ。
やっぱり知識が増えると楽しい。
楽しくなるまでが大変だけど得られるものは大きい。