tamakipedia

フロントエンドエンジニア。Typescriptもう特訓中です。一日の振り返りや学んだことをちょっとずつ吐いています。

【Typescript】ジェネリクスについて

function test<T>(arg: T): T {

上記のHTMLタグのような表記のことをジェネリクスと言います。

ジェネリクスとは

Genericsは抽象的な型引数を使用して、実際に利用されるまで型が確定しないクラス関数インターフェイス、を実現するために用います。

具体的に

javascriptとある関数

function test(arg){
  return arg;
}

②型を追加

function test(arg: string): string {
  return arg;
}

ジェネリクスを追加して呼び出し時点で型を確定させる。

function test<T>(arg: T): T {
  return arg;
}

//呼び出し時に型を決定
test<string>("aaaaaaa")

インプットが全然追いつかないぜ!

日記です。

お久しぶりです。三ヶ月ぶりかな。

初めての転職が無事に終わりました。
前職とはまた違ったアウトプットを求められる職場で、毎日楽しいです。

職場の皆さんから多くのことを吸収する一方で、振り返る時間がなかなか取れず、
それでいて昔のように学んだことをまとめるような時間も取れなさそうなので.
今日からちょこちょこ日記のような形で呟いていこうと思います。

リハビリですね。

開発までの準備に色々時間がかかる件

最近はコンポーネント設計なるものを学びました。
「学んだ」と言うのは語弊がありますが、頭ごなしに実装する中で徐々に
実装前に理解しなきゃならない部分があるなと言うことに気づき
細かくコンポーネント命名、利用する定数、それぞれのコンポーネントがどのディレクトリにあるべきか
を意識するようになりました。

そこで出てきたのがクリーンアーキテクチャ
the best of 珍紛漢紛です

qiita.com

今でも意味わかってないですが、 一言で言うと「それぞれのコンポーネントがもつべき責務ごとに分類する設計」のことだと解釈してます。

記事読みながら掻い摘んで説明しようとしたが、ちょっとわからなくなったのでストップ笑

なんにせよ、これまでSSRとJavascirptベタがきしか知らなかった自分としては知らない世界すぎて
「うわぁああ」な毎日です。

ちょっとずつタスクの準備ができるようになった今日

さて、殴り書きは続きます。

そしてちょっとずつフロントのタスクを進めながら
ちょっとずつ開発準備にもっと時間をかけるべきだと言うことがわかってきました。

今週から意識していることはどんなに小さなタスクでも、細かく洗い出して質問できる粒度に持っていくこと。
質問できるのであればある程度理解が進んでいることにもなりますからね。
はい、それくらい粒度の大きい状態で開発に臨んでいたので、、笑 今週やっと気づきました。

### そして今日

今日は露出(バナー)系のコンポーネント設計を進めました。   それぞれが持つべき定数やコンポーネント命名などが完了。

明日はコンポーネントの呼び出す場所の洗い出し、バナーを消すばつボタンのキャッシュ操作に
最適なツールの調査を進めたいと思います。
頑張るぞ

【contentful】content field をマークダウン形式に設定する

contentful field というのは、contentful で利用されている入力項目の種類のことです。

こちらでマークダウン形式で入力できるようにフィールドを追加していきます。

手順

1 Add field ボタンでフィールドを追加

2 フィールドの種類は「text」を選択
(「Rich Text」ではないので注意。 )
f:id:okinawanpizza:20211113083726p:plain

3 「Long text」を選択して create and configure を選択
f:id:okinawanpizza:20211113084743p:plain

4 「Appearance」でフィールドの形式を Markdown に変更
f:id:okinawanpizza:20211113084944p:plain

以上

割とすぐ設定できたのでよかった^^

【vercel】【Next.js】vercelを使ってのデプロイ方法

vercelでのデプロイがうまくいかなかったので
最初から作りました。手順メモ。

特に難しくはないのですがポイントが二つあります。
* Create Team は skip すること
* .env.localで設定した環境変数を設定すること

方法

・vercelにアクセスし、下記画像からCreate New Projectを選択。 f:id:okinawanpizza:20211025023257p:plain

・デプロイするリポジトリを選択してimport f:id:okinawanpizza:20211025023430p:plain

・Create Team の設定は Skip を選択 (チームは有料機能なので、
14日間のお試し期間終了後に無料プランで再度デプロイし直すことに、、) f:id:okinawanpizza:20211025025243p:plain

・Environment variables を設定する ここは.env.local で設定した変数を2点入力します。
NAMEにキー、VALUEに値を入力します。

//.env.local
NEXT_PUBLIC_CONTENTFUL_SPACE_ID=xxxxxxxx
NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

f:id:okinawanpizza:20211025025329p:plain

・設定完了です。
(スクショが微妙な感じに、、) f:id:okinawanpizza:20211025025427p:plain

以上になります。

5分もかからないでデプロイできるってすごい!!

オブジェクトから値だけを取り出す方法

apiの値を変数に代入する際に、以下のような方法だと可読性が上がります。

const obj = {name: "ko", age: 23, female: true}

const {name, age, female} = obj;

console.log(name); // "ko"

{ var } に代入すると、オブジェクトのキーがvarの値だけを取得できます。

const {name, age, female} = obj;

コンポーネントにpropsを渡すときに、
このモジュール内で使用するapiの値を1行目で確認する役割もあるので便利です。

export default function ArticleDetails({ article }) {
// (ふーん。 サムネとタイトルと記事内容を扱うんだ...)
  const { thumbnail, Title, detail} = article.fields

  return (
    <div>
      <div className="banner">
        <Image 
          src={"https:" + thumbnail.fields.file.url}
          width={thumbnail.fields.file.details.image.width}
          height={thumbnail.fields.file.details.image.height}
          /> 
        <h2>{ Title }</h2>
      </div>
      <div className="detail">
        <h3>detail</h3>
        <div>{ documentToReactComponents(detail) }</div>
      </div>
    </div>
  );
};

おしまい

【contentful】Rich Text を表示するために documentToReactComponents関数 を使う

記事の詳細を作成するときに
記事内容をRich Text で記述するようにしました。
他の項目のようにpropsを渡すだけではなかったので方法を記述します。

contentfulの取得方法はこちら↓ https://okinawanpizza.hatenablog.com/entry/2021/10/21/010930

documentToReactComponents()

contentful側の関数で、リッチテキストの中身を読んでくれる関数のようです。

  • ライブラリをインストール
npm install --save contentful @contentful/rich-text-html-renderer
  • documentToReactComponentsをインポート
import { documentToReactComponents } from '@contentful/rich-text-html-renderer';
  • リッチテキストの項目を引数にして以下のように記述
export default function ArticleDetails({ article }) {
  const {Title, detail} = article.fields

  return (
    <div>
      <div className="detail">
        <h3>detail</h3>
        <div>{ documentToReactComponents(detail) }</div>
      </div>
    </div>
  );
};

これで記事詳細を出力できます。

参考
https://www.contentful.com/developers/docs/javascript/tutorials/rendering-contentful-rich-text-with-javascript/

おしまい

【Next.js】【contentful】動的なルーティングを利用した詳細ページの作成

こんにちは、本日はcontentfulのエントリーの内容を詳細ページに追加していこうと思います。
なお、エントリーは一記事単位の呼び方です。
(下記図参照)

f:id:okinawanpizza:20211024201207p:plain

結果

以下が全体図です。

//pages/posts/[slug].js
import { createClient } from "contentful";
import Image from "next/image";
import { documentToReactComponents } from "@contentful/rich-text-react-renderer"

const client = createClient({
  space: process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID,
  accessToken: process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN,
});

export const getStaticPaths = async () => {

  //article 全件取得
  const res = await client.getEntries({
    content_type: "article"
  })

  //全件のパラメーターだけを配列形式で取得
  //[ { params: { slug: 'test2' } }, { params: { slug: 'test1' } } ]
  const paths = res.items.map(item => {
    return {
      params: { slug: item.fields.slug }
    }
  })

  return {
    paths,
    fallback: false
  }
}

export async function getStaticProps({ params }) {
  
  const { items } = await client.getEntries({
    content_type: "article",
    "fields.slug": params.slug
  })

  return{
    props: { article: items[0] }
  }
}


export default function ArticleDetails({ article }) {
  const { thumbnail, Title, detail} = article.fields

  return (
    <div>
      //省略
    </div>
  );
};

Dynamic Routes(動的なルーティング)

Next.jsでは設定したファイル名が自動的にスラッグになります。
また[params].jsなどのようにしてファイル名に角括弧(ブラケット)を使うことで
動的なルーティングを作成できます。

つまり * pages/post/create.js/post/create にマッチします。 * pages/post/[sulg].js/post/1/post/abc にマッチします。

contentfulのインポート

contentfulをインポートします。

import { createClient } from "contentful";

spaceIDとaccessTokenを入力し
contentfulにアクセスするapiを取得します。

const client = createClient({
  space: process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID,
  accessToken: process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN,
});

それぞれのエントリーのslug名を全件取得

content_typearticleのエントリー(記事)をjson形式で取得、
それぞれのslugと言う項目だけを返すようにします。
下記のような形が返却されます。
[ { params: { slug: 'test2' } }, { params: { slug: 'test1' } ,....} ]

  //article 全件取得
  const res = await client.getEntries({
    content_type: "article"
  })

  //全件のパラメーターだけを配列形式で取得
  //[ { params: { slug: 'test2' } }, { params: { slug: 'test1' } } ]
  const paths = res.items.map(item => {
    return {
      params: { slug: item.fields.slug }
    }
  })

  return {
    paths,
    fallback: false
  }

ちなみにslugのcontentful側での設定は
content model から手動で設定しているものになります。
f:id:okinawanpizza:20211024214937p:plain

urlに該当するエントリーを取得

現在のurlに該当するエントリーをgetEntries関数で取得し
コンポーネント側にpropsを渡します。

export async function getStaticProps({ params }) {
  
  const { items } = await client.getEntries({
    content_type: "article",
    "fields.slug": params.slug
  })

  return{
    props: { article: items[0] }
  }
}

export default function ArticleDetails({ article }) {
  console.log(article)

  return (
    <div>
      //省略
    </div>
  );
};

以上です!、、思ったより手こずりました。
ただ、youtubeで英語でガッツリ解説してくれている動画を見つけて
そこから意外とスムーズにいきました。

おしまい