13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

React/Redux/redux-saga/TypeScript/Cloud Firestore/Cloud Functionあたりを使ってブログを作った!

Last updated at Posted at 2020-05-04

こんにちは、アオキです。

今回、タイトルにもありますが
React/Redux/redux-saga/TypeScript/Cloud Firestore/Cloud Function
あたりの技術を使って
Atomic Design
っぽい構成の
ブログサイトを作成したので、工夫した点・苦労した点など書いていければと思います。

実際に作成したサイトがこちらになります
オープンソースとなっていて、githubリポジトリはこちらになります。

ざっくり、こんな感じのUIです↓
スクリーンショット 2020-04-13 17 01 35

はじめに

皆さんはブログを書きたい!と思ったことはありますでしょうか?
人生で一度くらいはありますよね。(?)

僕にもその時期が来ました。

そこで、はてブとかに書こうかなーと考えていたのですが、
せっかくの機会なので自分でオリジナルのブログを作ろうと思いました。

アーキテクチャ

載せる必要ないかもしれないですが、アーキテクチャはこんな感じです。

スクリーンショット 2020-04-15 16.49.21.png

SPAなサイトで、サーバレスです。

Cloud Functionsは、OGP画像の設定のみに利用しています。

また、デザイン手法として、Atomic Designを採用しました。
Atomic Designについては、こちらの記事がわかりやすいです。

主要機能

今回作成したブログサイトの主要機能は以下の通りです。

・記事の投稿/編集/削除/取得
・タグ機能
・いいね機能

まぁ普通のブログサイトにはあるような機能ですね。
それぞれ、簡単に使用したライブラリなどを説明していきます。

・記事の投稿/編集/削除/表示

こちらはFireStoreに用意されているメソッドを叩くだけです。
公式ドキュメントに詳しく書いてあります。

例えば記事の取得に関して、以下のようなコードになっています。

記事を全て取得するコード
export const getArticles = async () => {
  try {
    const articles: Model.Article[] = [];
    await firebase
      .firestore()
      .collection("articles")
      .orderBy("date", "desc")
      .get()
      .then(snapshot => {
        if(snapshot.empty) {
          return;
        }
        snapshot.forEach(doc => {
          articles.push({
            uid: doc.id,
            content: doc.data().content,
            subTitle: doc.data().subTitle,
            title: doc.data().title,
            date: doc.data().date.toDate(),
            tagIds: doc.data().tagIds,
            goodCount: doc.data().goodCount,
            thumbnailImagePath: doc.data().thumbnailImagePath
          });
        });
      })
      .catch(err => {
        throw new Error(err.message);
      });
    return { articles };
  } catch(error) {
    return { error };
  }
};

このあたりに書いています

⚠︎ このソースのコーディングがペストプラクティスな書き方とは限りません(以下同様)

・タグ機能

react-tag-inputという、ライブラリを利用しました。
以下のようなサジェストも簡単に実装できて、便利でした!
tag.gif

サジェストを表示するコード
import React, { FC, useState, useEffect } from "react";
import { WithContext as ReactTags } from "react-tag-input";

// import css
import "./react-input-tag.css";

// import methods
import * as tagMethod from "methods/tagMethods";

// import models
import * as Model from "models/tagModel";

interface DefaultProps {
  onBlur: (tags: { id: string; text: string; }[]) => void;
  defaultTags?: Model.Tag[];
}

const KeyCodes = {
  comma: 188,
  enter: 13
};

const delimiters = [KeyCodes.comma, KeyCodes.enter];

const AdminCreateArticleTags: FC<DefaultProps> = ({ onBlur, defaultTags }) => {
  const [tags, setTags] = useState<Model.Tag[]>(defaultTags ? defaultTags : []);
  const [suggestions, setSuggestions] = useState<Model.Tag[]>([]);

  useEffect(() => {
    getData();
  }, []);

  const getData = async () => {
    const data = await tagMethod.getTags();
    setSuggestions(data);
  };

  const handleDelete = (i: number) => {
    setTags(tags.filter((tag: Model.Tag, index: number) => index !== i));
  };

  const handleAddition = (tag: Model.Tag) => {
    setTags([...tags, tag]);
  };

  const handleInputBlur = () => {
    tags.forEach(tag => {
      tagMethod.addTag(tag.text);
    });
    onBlur(tags);
  };

  const handleDrag = (tag: Model.Tag, currPos: number, newPos: number) => {
    const newTags = tags.slice();

    newTags.splice(currPos, 1);
    newTags.splice(newPos, 0, tag);
    setTags(newTags);
  };

  return (
    <ReactTags
      tags={tags}
      suggestions={suggestions}
      handleDelete={handleDelete}
      handleAddition={handleAddition}
      handleDrag={handleDrag}
      handleInputBlur={handleInputBlur}
      delimiters={delimiters}
    />
  );
};

export default AdminCreateArticleTags;

このあたりに書いています

・いいね機能

スクリーンショット 2020-05-04 17.05.34.png ↑みたいな感じで、いいね数を把握するために、firestoreに一つフィールドを追加してあります。

それを更新していく形で実装しました。

コードに関しては、記事の更新とほとんど変わらないので、省略します。

開発してみて

実は、上で挙げた主要機能以外のところにもこだわっていて、

・画像を圧縮してストレージに保存(canvasを利用し、クライアント側で画像を圧縮)
・OGP画像のために、Cloud Functionsを利用
・無駄なimportを減らすために、動的なimportを採用
・記事を5件読み込み、スクロールに応じて5件ずつ取得する機能

などです。

このような機能群を、0から個人開発することで、成長することができました。

皆さんも、何か作ってみたい!と思えるものがあれば是非作ってみてください!

以上になります。

13
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?