
😱 react初心者あるある
こんな悩みを持つ人の支援でありたい。
- jsxにHTML/CSSを書くことはできる。もっと書けるようになりたい。
- 何が分からないか分からない。
- エラーが出たらスグにググってる。
- 最終的にreactで個人開発したい。
⚡️ react学ぶ価値を再確認
事実:
- reactは世界的に大人気
- react学べば、WEBサービスを作れる時代
- 派生しているreact nativeを使えばアプリも作れる
空想:
- react学べば、食いっぱぐれない
- WEBサービスもアプリも個人開発できるようになるかも
💻 手順
- Nextjsのcontentful exampleから始める
- 記事ページを表示する
- 記事ページでcontentfulのマークダウンをレンダリングする
2の段階でぶつかった壁を記事にしていきます。
1.Nextjsのcontentful exampleから始める
VercelのテンプレートからNextjs+contentfulを立ち上げます。 https://vercel.com/import/templates
contentfulの設定などドキュメントはこちらを参照。 https://vercel.com/guides/deploying-next-and-contentful-with-vercel
ひとまず記事一覧ページが作れるかと思います。
2.記事ページを表示する
ここからはexampleから進んで、記事本文のページを作っていきます。
nextは /blog/[slug].jsx
でslugに応じたページを生成してくれます。
next/routerを使ってurlのslugを取得し、そのslugでcontentfulの記事を取得・表示します。
こんな階層構造に。
早速、Next.jsのrouterでハマりました。
URLのslugをもとに、contentfulの記事を取得しますが、ハマってしまいました。
学んだこと
- next/routerが初回は取得できないこと
- useEffect第二引数に指定した値に変化があったら、再実行してくれること
- HTML描画時にエスケープしないと、1と2の影響でエラーになる
参考にさせていただいたのはこちら
- https://ryotarch.com/javascript/react/next-js-router-query-undefined-on-first-rendering/
- https://github.com/vercel/next.js/discussions/11484
結果、このような書き方にして、なんとか表示できました。
import { useEffect, useState } from "react";import { useRouter } from 'next/router'import Header from "../../components/Header";import Footer from "../../components/Footer";import {createClient} from "../../components/contentful";const client = createClient()function BlogPost() {const router = useRouter()const [queryId, setQueryId] = useState(null)const [post, setPost] = useState({});useEffect(()=> {if(router && router.pathname) {setQueryId(router.query.slug)}}, [router])async function fetchEntries() {const entries = await client.getEntries({content_type: "blogPost",limit:1,'fields.slug': queryId})if (entries.items[0].fields.slug === queryId) {return entries.items[0].fields}// todo! return 404}useEffect(()=> {async function getPost() {const targetPost = await fetchEntries();setPost(targetPost);}getPost();}, [queryId])return (<><Header />{post ?<>{console.log(post)}{post.category ? post.category.fields.name : ''}{post.title}{post.heroImage ? <img src={post.heroImage.fields.file.url} alt=""/> :''}{post.publishDate}{post.body}</>: '404'}<Footer /></>);}export default BlogPost;
まずはここまで表示できました。
3.記事ページでcontentfulのマークダウンをレンダリングする
MDXを入れたかったけど断念しreact-markdownを導入しました。 syntax highlightは React Syntax Highlighterを導入。
学んだこと
- readmeに記載の読み込み方では読み込めず、
esm
をcjs
に書き換える必要がありました。https://github.com/react-syntax-highlighter/react-syntax-highlighter/issues/230 - 好みのカラーはこちらで選べます。https://react-syntax-highlighter.github.io/react-syntax-highlighter/demo/prism.html
インストール
npm i react-markdownnpm i react-syntax-highlighter
マークダウン変換
import * as Markdown from 'react-markdown'import RenderOptions from "../../components/RenderOptions"~function BlogPost() {return (<>~<Markdown source={post.body} renderers={RenderOptions} />~</>)}
RenderOptions.js にてカラー指定。普段使っているVScodeのカラーにしました。
import React from 'react'import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter'import {vscDarkPlus} from 'react-syntax-highlighter/dist/cjs/styles/prism'const RenderOptions = {code: ({language, value}) => {return <SyntaxHighlighter style={vscDarkPlus} language={language} children={value} />}}export default RenderOptions;
こんな感じでマークダウンとシンタックスハイライトが機能しました。
↓こちらもぜひ参考にしてみてください↓
