かめ。ブログ

microCMS × Reactで吹き出しで会話してるUIを作る

2021年12月29日
2022年4月15日

目次

アブブ

こんにちは。

よろしくおねがいします!

上みたいな感じで、Wordpressでよく見る形の、ふきだしで会話をしている風なものを、microCMSとReactでカスタムフィールドを使って作ってみました。
下のWordpressのプラグインのような感じですね。


その手順と、Reactでのコンポーネントの作り方について解説します!

前提

  • microCMSを使って記事用のapiが製作済み


管理画面の使い勝手


カスタムフィールドを使ってこのような形になるように作っていきます。

記事用APIのカスタムフィールドを設定

右上カスタムフィールドをクリック

カスタムフィールの基本情報を入力


カスタムフィールド名: comment
カスタムフィールドID: comment
としています。

APIスキーマ(インターフェース)を定義

フール度ID表示名種類
image画像画像
name名前テキストフィールド
textテキストテキストフィールド
isRightコメント位置(右の場合ON)真偽値

項目名でなんとなくはわかるかと思います。 isRightがちょっとわかりづらいかもですが、**画像を左に表示するか、右に表示するかを設定するものです。

アブブ

右の場合はボタンをONにする!そうすると右からの発言になります!

アブブ

OFFの場合はこっちから!

コメント繰り返し用のカスタムフィールドを作る


先ほど作った「comment」のカスタムフィールドを「繰り返し」で設定します



記事API側で選択できるように

記事側のbodyを「繰り返しフィールド」にしてMarkdownで書けたりcommentsを設定できたりできるようにいくつか用意しています。
今回作ったコメント機能以外にも、拡張できるようにしています。

React側でそれに対応したコンポーネントを作る

使っているもの

  • styled-component。

型定義

export interface Body {
  fieldId: string
  value: string
}
export interface Comments extends Body {
  comments?: CommentBody[]
}
export interface CommentBody extends Body {
  image?: ImageInfo
  text?: string
  isRight?: boolean
  name?: string
}

Comment.tsx

import React from 'react'
import Image from 'next/image'
import { CommentBody } from '../entry/Entry'
import styled from 'styled-components';

const CommentContainer = styled.div<CommentBody>`
  margin: 24px 5% !important;
  display: flex;
  flex-direction: ${(p) => p.isRight ? 'row-reverse' : 'row'};
  align-items: flex-start;
  vertical-align: top;
`

const Message = styled.p<CommentBody>`
  margin: 0 !important;
  width: 100%;
  border-radius: 6px;
  border: 2px solid #ccc !important;
  margin-left: ${(p) => p.isRight ? '' : '16px'} !important;
  margin-right: ${(p) => p.isRight ? '16px' : ''} !important;
  padding: 16px !important;
  position: relative;
  &:before {
    position: absolute;
    top: 16px;
    left: ${(p) => p.isRight ? '' : '-7px'};
    right: ${(p) => p.isRight ? '-7px' : ''};
    display: block;
    content: '';
    transform: rotate(45deg);
    border: 2px solid #ccc;
    border-top: ${(p) => p.isRight ? '' : 'none'};
    border-right: ${(p) => p.isRight ? '' : 'none'};
    border-bottom: ${(p) => p.isRight ? 'none' : ''};
    border-left: ${(p) => p.isRight ? 'none' : ''};
    width: 10px;
    height: 10px;
    background-color: #fff;
  }
`
const Icon = styled.figure`
  margin: 0;
  vertical-align: top;
  width: 100px;
  font-size: 1px;
  & img {
    border-radius: 6px;
  }
`
const Name = styled.p`
  font-size: 11px;
  padding: 0 !important;
  margin: 0 !important;
  border: none !important;
  text-align: center;
`

export const Comment: React.FC<CommentBody> = props => {
  return (
    <CommentContainer {...props}>
      {props.image && <Icon>
        <Image src={props.image.url} width={props.image.width} height={props.image.height} alt="" />
        {props.name && <Name>{props.name}</Name>}
      </Icon>}
      
      <Message {...props}>{props.children || props.text}</Message>
    </CommentContainer>
  )
};


export default Comment


基本的には仕組みは、先ほど作ったカスタムフィールドのAPIで帰ってくる値を元にコンポーネントを作っています。
isRightの場合は画像が右側に表示するようにstyled-componentにpropを渡してスタイルを変更するように書いています。

ちょっと!importantを使っちゃってるところもあります。。あまり良くないですね;
使ってる理由はこのコンポーネントが描画されるところでは自動的に良い感じのスタイルがつくようになっているので
むりやり上書きする形にしています。

なぜ良い感じのスタイルがつくようになっているかというと、
Markdownなどで変換したhtmlにスタイルを当てたいためそのような仕様にしています。

コメントコンポーネントを使う側のコード

      <EntryBody>
        <div ref={entryBody}>
          {props?.body?.map((v, i) => {
            const commentValue = v as Comments
            if(v.fieldId === 'comments') {
              return commentValue.comments!.map((v) => <Comment {...v} />)
            }

            return (
              <div
                key={`${v.fieldId}${i}`}
                dangerouslySetInnerHTML={{
                  __html: v.fieldId === 'html' || v.fieldId === 'markdown' ? marked(v?.value) : v?.value,
                }}
              />
            )
          })}
        </div>
      </EntryBody>


bodyapiから取得してきた、繰り返しの設定を行った配列が返ってきます。
fieldIdにカスタムフィールド作成時に設定したカスタムフィールド名が返ってきます。

返ってきた値によって。表示するコンポーネントを変えてあげることで、さまざまなコンポーネントを作ってUIを再現することができます。

まとめ

他にもいろいろなコンポーネントをカスタムフィールドを使って作ることができると思うので
いろいろな便利なUIを作ってブログを面白くしていきたいです!

アブブ

頑張っていきます!