react(Next14)備忘録
nextjs App setup
$ npx create-next-app {project-name}
カレントディレクトリに作成する場合
$ npx create-next-app .
App Router
RSCの理解やPage Routerとの違いを理解するときは↓の記事
nextjsにおけるルーティングの仕方(App Router)
.
└── app/
├── blog/
│ ├── [postId]/
│ │ └── page.tsx
│ └── page.tsx
├── page.tsx
└── layout.tsx
- page.tsxを作成すると作成したディレクトリの名前でルーティングされる
- [id]のようにするとid個別のページが作成される
- layout.tsxでpage.tsxをラップする
その他、特別な意味を持つディレクトリは冒頭記事参照。
まだ サーバーコンポーネントとか、クライアントコンポーネントとかの理解が追いついていないので、学習できるようなサンプルアプリケーションを作りながら学べればいいなぁ...
html-react-parser
リッチエディタなどの出力でhtml文を出力する際、パースが必要になる。
そのためのreactライブラリ。
$ npm i html-react-parser --save
import React from 'react'
import parse from 'html-react-parser' //インポート
const blogs = () => {
return(
<div>
{parse(<p>テキストテキストテキスト</p>)}
</div>
)
}
export default blogs
ループ(ブログ一覧取得など)
import React from 'react'
import Link from 'next/link';
import { getList } from '@/app/libs/client'
import parse from 'html-react-parser'
const blogs = async () => {
const { contents } = await getList() //APIを叩いてリスト形式のJSON取得
return (
<div>
<ul>
{contents.map((post) => { //ループ開始
return (
<Link href={`/blog/${post.id}`}>
<li key={post.id}>
<h2>{post.title}</h2>
{parse(post.body)}
</li>
</Link>
)
}
)}
</ul>
</div>
)
}
export default blogs
async / awaitで非同期処理。
contentsの中身:
"contents": [
{
"id": "vzlgvybt8bia",
"createdAt": "2024-05-15T05:04:27.827Z",
"updatedAt": "2024-05-15T05:04:27.827Z",
"publishedAt": "2024-05-15T05:04:27.827Z",
"revisedAt": "2024-05-15T05:04:27.827Z",
"title": "テストタイトル2",
"body": "<p>テキストテキストテキスト</p>"
},
{
"id": "x6-a050fa",
"createdAt": "2024-05-14T11:52:33.557Z",
"updatedAt": "2024-05-14T11:52:33.557Z",
"publishedAt": "2024-05-14T11:52:33.557Z",
"revisedAt": "2024-05-14T11:52:33.557Z",
"title": "テストタイトル1",
"body": "<p>テストテキストテストテキストテストテキスト</p>"
},
],
配列で返されるので、map関数で取り出していく。
<ul>
{contents.map((post) => { //ループ開始
return (
<Link href={`/blog/${post.id}`}>
<li key={post.id}> //キーをidで指定
<h2>{post.title}</h2>
{parse(post.body)} //html形式なのでparse
</li>
</Link>
)
}
)}
</ul>
Next14でsass & css modulesを使うとき
Sassコンパイラのインストール
$ npm i sass --save
CSS Modules使う時用のプラグインインストール
$ npm i typed-scss-modules --save
設定ファイル作成 & グローバル変数使う時用の設定追記(variables.scssがグローバル変数記述ファイル)
export const config = {
exportType: 'default',
nameFormat: 'none',
implementation: 'sass',
additionalData: `@use "src/styles/variables.scss" as *;`,
ignore: ['**/variables.scss', '**/variables/**']
}
注意点:
・CSS module内ではimpureなセレクタがトップレベルでは使用できない
html body ul など。
html{
overflow-x:hidden
}
↑みたいにするとエラー吐くので、idつけるなりクラスつけるなりして対応。
(あっているのか不安)
↑だけだと足りなかったので追記。
Classの型定義をしてクラスの補完が効くようにする。
$ npm i npm-run-all --save
"scripts": {
"dev": "run-p dev:next dev:scss",
"dev:next": "next dev",
"dev:scss": "typed-scss-modules src --watch",
"typegen:scss": "typed-scss-modules src"
}
この辺をいじってると、npm run devなどのCLIコマンドで何をしているかがなんとなくわかってくるようになった。
「"script" :{} 内に書いてある"prefix"でnpm run {"prefix"} が決まって、そこでもともとインストールされているCLIコマンドを実行しているんだなぁ」とか。
(言語化むずすぎる)
個人的に、ライブラリやパッケージはできるだけ使いたくない(過去の経験から保守性が低くなる)ので、なんとかしないようにしていたけれど、「ライブラリを使う = 保守性が下がる」ではなく、「ライブラリをインストールする分理解しなければならないことが増える」だけであることに気づいた。
そもそもNextなどのプログラムがどのようにコンパイル or ビルドされているか等の理解度が低ければなんのライブラリ使っても保守が難しくなるのは当たり前で、それを当たり前のように理解して開発していくのがエンジニアと呼ばれる人たちなんだなぁとか思ったり。
コンポーネント ←→ ページ間の情報をpropsで受け渡し
コンポーネント:
import React from 'react'
import Link from 'next/link';
import parse from 'html-react-parser'
import styles from './ArticleCard.module.scss'
type Props = {
id: string;
title: string;
body: string;
}
const ArticleCard = (props: Props) => {
return (
<li key={props.id} className={styles['c-articleCard']}>
<Link href={`/blogs/${props.id}`}>
<h3 className={styles['c-articleCard--title']}>{props.title}</h3>
{parse(props.body)}
</Link>
</li>
)
}
export default ArticleCard
コンポーネント側でpropsを引数にもつ
型定義をするために type Props ={}内に定義。 anyは敗者。
ページ:
import React from 'react'
import { getList } from '@/app/libs/client'
import ArticleCard from '@/public/components/card/ArticleCard';
import styles from './FrontPage.module.scss'
const FrontPage = async () => {
const { contents } = await getList()
return (
<div className={styles['p-frontPage']}>
<ul className={styles['p-frontPage__articleList']}>
{contents.map((post) => {
return (
<ArticleCard
{...post}
/>
)
}
)}
</ul>
</div>
)
}
export default FrontPage
ページ側で渡す値を書いていく。
本来は
<ArticleCard
id ={post.id}
title = {post.title}
body ={post.body}
/>
のように書いていくけれど、情報を限定しない場合は{...post}のように省略できる(スプレッド構文)
参考:ja.react.dev