こんにちは!いまだにreturnの綴りが怪しいフロントエンドエンジニアです。今回はNext.jsの概念をざっくりとまとめたいと思います。(Reactと重複する内容有/今回は概念だけですので、詳しい書き方については書いていません。)
その前にまず私の状況
今までVue.jsしかやったことがなく、React系を始めて1か月経ったところです。そのためかなり知識が未熟なところがあります。他のブログ/本/動画などを参考にし、補助的にこちらを見て頂ければと思います。
そのような状況で今回記事を書いた理由は、
・「とりあえず書き始めたい!」という人向けの記事が少ないように感じたため
・TypeScriptのNext.jsの資料が少ない
というところです。
サイトパフォーマンス等一旦気にせず、とりあえず始めたいという人向けになるべく分かりやすく説明したいと思います。
目次
1.基本的な概念
2.ページについて
3.拡張子について
4.use/memoで囲む
5.型について
6.APIについて
7.まとめ
8.おまけ
1. 基本的な概念
■ファイルの中身
大体1ファイルの中身はこのような構成です。拡張子については後ほど説明します。
■コンポーネントについて
Nextは「とにかく部品を作って、それをページに埋めていく!」という考えのもと作っていきます。(ここで言う「ページ」は実際にサイトとして見せる用のファイルの事)
例えば、ボタンを3つ使いたいページを作る際に。。。
そのため、部品(コンポーネントファイル等)を1つ作成して、それを使いたい度に呼び出してページに埋めていきます。
■コンポーネント以外の部品達
「部品」はコンポーネントの他にも色々あります。
・コンポーネント:HTMLにて埋め込む部品(ボタン/フォーム/ヘッダー等々…)
・カスタムhook:色々なところで使いたいメソッド
・プロバイダー:色々なところで使いたい値
・タイプ:型をまとめておく 等々…
それぞれのファイルの置き場所はこのような感じです。
hooksフォルダを作成して、その中にカスタムhookファイルを作成するイメージ
※ちなみにカスタムhookのカスタムは、Nextに元々用意されているhookがあるので、自分で作成したhookの事をカスタムhookと呼んでいます。
■コンポーネントの作り方
細かいところは後ほど説明していきます。一旦コンポーネントの書き方のみ紹介です。
//Buttonというコンポーネント
//export const コンポーネント名:型(()=>{ページの内容})
export const Button: FC = memo(() => {
//これを埋め込んだ際に返すHTML(このHTMLが表示される)
return ( <><button>ボタン</button></> ); });
<!--埋め込みたいところにこれを書けば埋め込める-->
<Button/>
という事でこのように色々な部品を埋め込んで1ページを作成していくイメージになります。
■コンポーネントに値を渡す:Props/Children
このような問題を解決するために、Propsという概念が存在します。
Propsは一旦コンポーネント内の値を「仮」にしておいて、ページで使う際に入れる値を決定出来るシステムです。
//最初に受け取るPropsの型を宣言
export type Props = {
label: string;
};
//propsを使用する際には()の中にpropsを入れる
export const Button: FC<Props> = memo((props) => {
//Propsで受け取る値を宣言
const { label } = props;
//これを埋め込んだ際に返すHTML(このHTMLが表示される)
//ここのlabel部分に渡された値が入る
return ( <><button>{label}</button></> ); });
<!--ボタンコンポーネントのラベルに「検索」を渡す-->
<Button label="検索" />
同じようにChildrenという概念も存在します。
これは挟んで値を渡してあげるやり方です。渡す値が大きい範囲の際はこちら利用すると良いかも。
//ここの書き方は固定
type Props = {
children: ReactNode;
};
//propsを使用する際には()の中にpropsを入れる
export const Button: FC<Props> = memo((props) => {
//Propsで受け取る値を宣言
const { children } = props;
return ( <><button>{children}</button></> ); });
<!--ボタンコンポーネントのchildrenに「検索」を渡す-->
<!--挟んだ値が渡される-->
<Button>検索</Button>
1のまとめ
・Nextは部品を作成してページに埋めていく
・コンポーネント:HTML/hooks:メソッド/providers:値/types:型
・コンポーネントはProps/Childrenで値を渡せる
2. ページについて
先程「ページはサイト表示用ファイル」と述べましたが、超厳密にいうと表示しているのは「ページを埋めたレイアウトコンポーネントを埋めた_app.jsx」です。ここを理解するのもなかなか難しい。。
それぞれの書き方も見ていきたいと思います。
■ページファイル
コンポーネントは上でexportしてたけど、ページは下でexportする。
//const ページ名 :型
const Page: NextPage = () => {
return (
<>ページの内容</>
);
};
export default Page;
■レイアウトコンポーネント
Childrenのところにページファイルが入る
export const Layout: FC<Props> = memo((props) => {
const { children } = props;
return (
<>
<Header/> <!--ヘッダーコンポーネント-->
<main>{children}</main>
<Footer /> <!--フッターコンポーネント-->
</>
);
});
■_app.tsx
Headに書く事なければ初期のままでOK
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<Head>
<title>サイトのタイトル</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/image/favicon.ico" />
</Head>
<Component {...pageProps} />
</>
);
}
export default MyApp;
2のまとめ
・全体に表示させたい内容はレイアウトコンポーネントにて
・全体の設定(Headタグ等)は_app.tsxにて
・コンポーネントは上でexport/ページは下でexport
3. 拡張子について
ファイルの拡張子なのですが、.ts(.js)と.tsx(.jsx)が存在します。
この違いは「そのファイルは、最後何でreturnしているか」です。
・値/メソッド等、TypeScript形式でreturn→.ts(カスタムhook等)
return num1;
・HTMLの形でreturn→.tsx(ページ/コンポーネント等)
return (<button>{label}</button>);
3のまとめ
・「そのファイルは、最後何でreturnしているか」でファイルの拡張子を決める
・HTML → .tsx
・値/メソッド → .ts
4. use/memoで囲む
Nextでは以下のuse/memoを使って、変数等を囲む必要があります。
それぞれの理由を説明すると長くなるのでここでは置いておきます。
囲む対象 | 使うもの |
---|---|
メソッド外で宣言している変数 | useState |
関数 | useCallback |
コンポーネント | memo |
(※使いたいときのみ)関数:値の変更→発動 | useEffect |
囲む/囲まないを場合によって判断する必要がある様ですが、その判断基準が人によって割とまちまちなため、一旦は全てにつけて問題ないかと思います。とにかく、コンポーネントはmemo/変数はuseState/関数はuseCallbackで囲む!です。(useEffectは使いたいときのみ…)
■変数:useState
//この先aaaの中身を初期値から書き換えない時
const [aaa] = useState("初期値")
//この先aaaの中身を書き替える予定の時
//後ろの値はset変数名の形にする
//letでなく、constでOK
const [aaa,setAaa] = useState("初期値");
//aaaの中身を書きかえる時
setAaa("書き換えたい内容")
///NG!
aaa = "書き換えたい内容"
メソッド外で宣言している変数にはuseState必要だけど、
とあるメソッド内でしか使っていない場合は不要
//num1はuseState必要
const [num1,setNum1] = useState(0)
const aaa = useCallback(()=>{
//num2はこのメソッド内でしか使っていないのでuseStateいらない
const num2 = 1;
setNum1(num2);
},[num1,setNum1])
■関数:useCallback
後ろの[]には、処理に使っている値が入ります。
VScodeのESLintを使っていれば恐らく下線が引かれるので、クイックフィックスで直せます。。
//囲まない場合
console.log("関数");
const aaa =()=>{console.log("関数")}
//囲む場合
useCallback()=>{console.log("関数");},[]);
const aaa = useCallback(()=>{console.log("関数");},[]);
■コンポーネント:memo
これを
export const Button: FC = () => {
return ( <><button>ボタン</button></> ); };
memo()で囲んでこうする
export const Button: FC = memo(() => {
return ( <><button>ボタン</button></> ); });
■useEffect
[]の中の値が変更された際に発動する。
[]を空にするとmount時(このページが作られる際)に自動発動
useEffect()=>{console.log("関数");},[num1]);
const aaa = useEffect(()=>{console.log("関数");},[num1]);
4のまとめ
・コンポーネントはmemo/変数はuseState/関数はuseCallbackで囲む!
・useEffectは値の中身が変更されたタイミングで発動/始めに自動発動させることが出来る
5. 型について
ここがNextの資料はTypeScriptで書かれているものが少なく、苦労した点です。。
ざっくり、覚えておくと良さそうな型を紹介します。
■ページファイル
NextPageをつける
const AaaPage: NextPage = () => {}
■コンポーネントファイル
FCとVFCがあるようですが、私はFCを使用しています。
//propsなし
export const Button: FC = memo(() => {})
//propsあり
export const Button: FC<Props> = memo((props) => {})
■useStateで型を設定する時
const [aaa]=useState<string>("");
■propsで定義する際に役に立ちそうな型
export type Props = {
title: string; //ここに入れる型
};
//メソッド
method: () => void;
//文字
mozi: string;
//数
id: number;
//true・false
isOpen: boolean;
//2種類のどちらかしか入らない時
color: "赤" | "青";
//onChange(ユーザが値を入力して、値の中身が変わったら発動する)
onChange: ChangeEventHandler<HTMLInputElement>;
//JSX(HTMLっぽい形を受け取る際)
contents:ReactNode;
■APIでとってきた値に型を設定する
//types>type.tsで型の設定をしておく
export type Mailtype = {
email: string;
};
//APIで取得したデータ:dataの中の「メール」に型をつける
const data: Mailtype = data.userMail;
6. APIについて
皆さんおなじみ(?)axiosやfetchで呼べるのですが、こちらもデータを取得の際は「APIを呼ぶタイミング」によって囲まなくてはいけません。ここについてはまだ私も勉強不足なので異なる点があればご教示ください。
呼ばれるタイミング | 用途 | 対象データ | |
---|---|---|---|
SSG | ビルド時 | 初期表示用 | まず更新する予定のないもの あまり書き直さないもの (ブログ等) |
SSR | サーバサイド | 初期表示用 | ユーザの操作をDBに 反映させる可能性があるもの (Twitterのタイムライン, アカウント情報等) |
CSR | クライアントサイド | 再取得用 | データ取得をマメにする 必要があるもの (Twitterのタイムライン等) |
とにかくまずは
・静的なページ→SSG
・動的なページ(ユーザの操作をDBに反映する事が多い)→SSR+CSR
という使い分けで問題ないかな、と思います。
※追記
Next.jsと(使った事ないので曖昧ですが)Nuxt.jsにはこの概念が存在しており、React,Vueは全てCSRの認識です。
7. まとめ
1.ページと部品
Nextは部品を作成してページに埋めていく
・コンポーネント:HTML
・hooks:メソッド
・providers:値
・types:型
コンポーネントはProps/Childrenで値を渡せる
2.出力ページの構成
・全体に表示させたい内容はレイアウトコンポーネント
・全体の設定(Headタグ等)は_app.tsx
・コンポーネントは上でexport
・ページは下でexport
3.拡張子
「そのファイルは、最後何でreturnしているか」でファイルの拡張子を決める
・値、メソッド → ts
・HTML → tsx
4.囲めルール
・コンポーネントはmemo
・変数はuseState
・関数はuseCallbackで囲む!
5.型について
特になし。
6.DBからデータの取得
APIからデータを取得する方法はタイミングによって書き方が異なる。
・SSG→静的なページの初期表示用(早い)
・SSR→動的なページ初期表示用(遅い)
・CSR→動的なページ リアルタイム更新用(遅い)
今後追加したい項目:
・onChange/onClick等イベントハンドラーについて
・mapで回す
ここまで記事をお読みいただきありがとうございました!
8. おまけ
Next.js/Reactを勉強するにあたり読んだ資料達
■本
■Next.js公式チュートリアル
■YouTube
(さいごに:
色々教えてくれたお二人ありがとうございました!@suzu1997 @hiroki-yama-1118)