Torihaji's Growth Diary

Little by little, no hurry.

初めての個人開発日記 8日目

はじめに

みなさん、こんにちは 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]

GitHub - torihazi/diary_front

GitHub - torihazi/diary_back

まずは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なものはいくつかあるが、これを選んだのは理由がある。

実務で使用しているからというのとそれなのに理解が浅いからそれを克服するために。

Getting started

以前書いたものを参考に思い出しながら。

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して読ませれば行けることは実務の経験上知ってるからなんとかなりそうだけど。

終わりに

でもまぁよく頑張った。一旦。

今日は終わりにしよ。

やっぱり知識が増えると楽しい。

楽しくなるまでが大変だけど得られるものは大きい。