はじめに
こんにちは、torihaziです
週末リリース予定ですが、果たして終わるのでしょうか。
今日も時間は短いですが、やっていきましょう
技術選定
[frontend]
- Nextjs(pages router) => App Routerの理解に苦しんだため
- MUI => 調べたランキングでtopだったため(=> approuter では動かないそう。今後は要検討)
- react-hook-form => フォーム管理と言ったらこれでは?
- zod => 少しだけ使い慣れてるから
- axios => httpリクエスト送るのはこれでは?
- recoil => 状態管理、useStateのような扱いで使いやすい
- js-cookie => cookieを扱う上で使用。軽く、更新も頻繁にされている
- useSWR => データ取得用、
- Editorjs => WYSIWYGエディタ、学習のため導入。本サービスのキモ。
[backend]
- Rails 7 => 使い慣れているため
- devise => 定番だから
- devise-jwt => devise-token-authが古いらしいのでこっち
- letter_opener_web => 開発環境で確認メール確認用
[github]
続き
昨日は新規作成のところまで行きました
今日は日記のタイトルがまだformとして送信できないので今日はそこをやっていこうと思います
このuseSWRのfetcher、関数ににして外に括り出したほうがいいかも。
newするときのbound mutateの時に使いたいんだけど、これだと使えないや。
//
// create
//
export const createInputScheema = z.object({
title: z.string().min(1, "Required"),
content: z.string().nullish(),
});
export type createInputSchemaType = z.infer<typeof createInputScheema>;
export const postNewDiaryInput = async (
data: createInputSchemaType
): Promise<Diary> => {
const newData = { diary: { ...data } };
return api.post("/api/v1/diaries", newData);
};
export const useCreateDiary = ({
onSuccess,
onError,
}: {
onSuccess?: () => void;
onError?: () => void;
}) => {
const setSnackBarState = useSetRecoilState(snackbarAtom);
const { data: diaries, mutate } = useSWR("/api/v1/diaries", getDiaries);
const createDiary = async (data: createInputSchemaType) => {
try {
const newDiary = await postNewDiaryInput(data);
mutate();
setSnackBarState(successState("作成しました"));
if (onSuccess) {
onSuccess();
}
return newDiary;
} catch (err) {
setSnackBarState(errorState("エラー"));
if (onError) {
onError();
}
throw err;
}
};
return { createDiary };
};
こんな感じに落ち着いたんだけどどうだろうか。
んー、なんか作成しましたのtoastは出るんだけど、あれだな。
ダメだ。
あたりまえだ。まだバックエンド作ってないや。
でもそしたらエラーに行ってくれないと困るんだけど。
なんで。
まぁいいか。
とりあえずbackendもできた。
def create
diary = current_api_v1_user.diaries.build(diary_params)
if diary.save
render json: diary, status: :ok
else
error_messages = diary.errors.full_messages.join(',')
render json: {message: error_messages}, status: :unprocessable_entity
end
end

一応、タイトルのところだけは formで囲んで、Editorはeditorだけで独立させた。
でreact-hook-formの管轄はタイトルだけだけど、submitする時に
editorのdataをくっつけて一緒に送る感じ。
import { DiaryTemplate } from "@/components/template/DiaryTemplate";
import { DiaryTitleForm } from "@/features/diaries/components/diary-title-form";
import {
createInputScheema,
createInputSchemaType,
useCreateDiary,
} from "@/lib/api/diaries";
import { OutputData } from "@editorjs/editorjs";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@mui/material";
import dynamic from "next/dynamic";
import { useRouter } from "next/router";
import { useState } from "react";
import { SubmitHandler, useForm } from "react-hook-form";
const Editor = dynamic(() => import("@/features/editorjs/editor-js"), {
ssr: false,
});
const DiaryNew = () => {
const [outputData, setOutputData] = useState<OutputData | null>(null);
const router = useRouter();
const { createDiary } = useCreateDiary({
onSuccess: () => {
router.push("/diaries");
},
});
const form = useForm<createInputSchemaType>({
mode: "onChange",
resolver: zodResolver(createInputScheema),
});
const onValid: SubmitHandler<createInputSchemaType> = (
data: createInputSchemaType
) => {
const newData = {
...data,
content: JSON.stringify(outputData),
};
createDiary(newData);
};
return (
<DiaryTemplate>
<DiaryTitleForm control={form.control} id="new-diary-title" />
<Editor value={outputData} onChange={setOutputData} holder="editorjs" />
<Button
type="submit"
form="new-diary-title"
onClick={form.handleSubmit(onValid)}
disabled={!form.formState.isValid}
>
保存する
</Button>
</DiaryTemplate>
);
};
export default DiaryNew;
このonValidのとこ。
で。

登録はできた。できたけど。
忘れてた。そうだった。contentを表示させるとこうなるのか。
どうするかな。
dataをやりくりして、なんとかして文字列に書き起こしてここに表示。
んー。そこまでやる必要あるかな。
ここのcontent消していいんでは?
消すか。
ていうかeditorjs の内容、空で送ったらnullになるんだけど。

あれかinitialなdataでもつけるようにするか。
というかなんかeditorjsの内容送られなくなった。
あれ、どこか変えたかな。
あーあれか
const onValid: SubmitHandler<createInputSchemaType> = (
data: createInputSchemaType
) => {
const newData = {
...data,
content: outputData === null ? JSON.stringify(outputData) : "", <= ここ
};
createDiary(newData);
};
多分ここ。outputDataが空白でもnullになることはない。
空なら
{
time: Date.now(),
blocks: [],
version: "2.30.6",
};
こんな空ではないデータが飛ぶから。
やっぱり。治したらうまく行った。
で、
import { OutputData } from "@editorjs/editorjs";
export const INITIAL_EDITOR_DATA: OutputData = {
time: Date.now(),
blocks: [],
version: "2.30.6",
};
あらかじめこれを
const [outputData, setOutputData] = useState<OutputData>(INITIAL_EDITOR_DATA);
const router = useRouter();
const { createDiary } = useCreateDiary({
onSuccess: () => {
router.push("/diaries");
},
});
~~~
このuseStateの初期値にsetする。
これなら、空として送信してもnullにならないのでは?
OK。blocksが[]になってる。

あとはここに表示させる時になんとかしよう。
これで新規作成終了。多分ね。次は更新か。
終わりに
寝てました。すいません。