Nextの続きです。前回はNextスターターキットの全体設定編でした。今回はコンポーネントやモジュールの設定をまとめていきます。Reactアプリ作成でよく使う基本的な機能を組み入れました。それではいきましょう!
【目次】
※参考:前回記事
【React】Nextスターターキットを作った-1. 全体設定編(Next + TypeScript + CSS in JS) - クモのようにコツコツと
※参考:ReactでWebアプリを作るシリーズまとめ
qiita.com
前回のおさらい
以前作成したReactスターターキットをNextスターターキットで作り直した。前回は全体設定編。
Reactスターターキットのときはうまくいかなかったページごとの固有なOGPも設定できた!今回はコンポーネントやモジュールの設定。
※参考:前回記事
【React】Nextスターターキットを作った-1. 全体設定編(Next + TypeScript + CSS in JS) - クモのようにコツコツと
作ったもの
ソースコード
※参考:GitHub - ryo-i/next-app-started
プレビュー
※参考:Nextアプリスターターキット
トップページ
ヘッダー、フッターのコンポーネント、テキスト「こんにちは、ねくすと」を表示するモジュールなどを今回設定している。 next-app-started.vercel.app
Aboutページ
※参考:このアプリについて | Nextアプリスターターキット
作者のプロフィール部分のコンポーネントを今回設定している。 next-app-started.vercel.app
ヘッダーコンポーネント
全ページのヘッダーに共通で表示するコンポーネント
ヘッダーテキスト
「/data/data.json」にヘッダーに表示するテキストを追加
{ // 中略 "header": { "title":"Nextアプリスターターキット", "text": "Next.js + TypeScript + CSS in JS環境" }, // 中略 }
「/components」フォルダに「Header.tsx」を追加し、ヘッダーテキストをインポート。
import Data from '../data/data.json';
テキストを変数title
、text
で取得
const title = Data.header.title; const text = Data.header.text;
ヘッダーCSS
ヘッダーのCSSを設定する。styled-components
、pageSize
、CSS変数cssVariables
をインポート
import styled from 'styled-components'; import { pageSize } from '../styles/mixin'; import cssVariables from '../styles/variables.json';
pageSize
、cssVariables
については前回記事を参照
※参考:【React】Nextスターターキットを作った-1. 全体設定編(Next + TypeScript + CSS in JS) - クモのようにコツコツと
HeaderTag
コンポーネントのCSSを設定
// Style const HeaderTag = styled.header` text-align: center; background: ${variable.bgColor_g}; .wrapper { ${pageSize} padding: 30px; } h2 { font-size: 2em; } nav span, nav a { padding-right: 0.5em; } `;
ヘッダーJSX
ヘッダー部分のJSXを設定する。
Next.jsのLink
をインポート
import Link from 'next/link';
Header
コンポーネント作成
// Component function Header() { return ( <HeaderTag> <div className="wrapper"> <h2>{ title }</h2> <p dangerouslySetInnerHTML={{ __html: text }}></p> <nav> <span>MENU:</span> <Link href="/"><a>Home</a></Link> <Link href="/about"><a>About</a></Link> </nav> </div> </HeaderTag> ); }
HeaderTag
コンポーネントに先ほどのCSS設定が適用される- h2タグで
title
、pタグでtext
を読み込む - navタグの中で
Link
コンポーネントを設定
最後にHeader
をエクスポート
export default Header;
フッターコンポーネント
こちらも全ページに共通で表示する
フッターテキスト
「/data/data.json」でフッターテキストを設定
{ // 中略 "footer": { "text": "©️ イイダリョウ" } }
「/components」フォルダに「Footer.tsx」を追加し、フッターテキストをインポート
import Data from '../data/data.json';
変数text
でフッターテキストを取得
const text = Data.footer.text;
フッターCSS設定
styled-components
とpageSize
をインポート
import styled from 'styled-components'; import { pageSize } from '../styles/mixin';
FooterTag
コンポーネントのCSSを設定
// Style const FooterTag = styled.footer` ${pageSize} padding: 30px; text-align: center; p { margin: 0; } `;
フッターJSX
Footer
コンポーネント作成
// Component function Footer() { return ( <FooterTag> <p dangerouslySetInnerHTML={{ __html: text }}></p> </FooterTag> ); }
FooterTag
コンポーネントに先ほどのCSS設定が適用される- pタグで
text
を読み込む
最後にFooter
をエクスポート
export default Footer;
hello()モジュール
hello()
モジュールはトップページにテキスト「こんにちは、ねくすと」を表示するだけのモジュール
data.json
「/public」フォルダに「data.json」を追加
{ "message": { "text": "こんにちは、ねくすと。", "selector": ".hello" } }
text
は表示するテキストselector
はテキストを読み込むセレクタ
今回、このファイルは仮想外部APIのレスポンスという想定でfetch APIで読み込みたいと思う。そのため、静的なファイル置き場である「/public」フォルダに追加した。
async/await(エラーハンドリングはtry-catch)
「/modules/hello」フォルダを作成し「hello.ts」を追加する。
変数url
で先ほどの「hello.json」をインポート
const url = 'data/hello.json';
hello()
モジュール作成
async function hello () { try { const res = await fetch(url); const hello = await res.json(); // fetch()がOKな場合の処理 } catch(err) { // エラー時の処理 } };
async/await
でhello()
関数を作成try
にfetch()
OKな場合の処理を書く
変数res
にawait
でfetch()
を実行、引数はurl
変数hello
にawait
でres
のレスポンスをjson()
で取得catch()
にエラー時の処理を書く
fetch()
はついthen()
で書きたくなっちゃうがなるべくasync/await
構文に慣れたい
※参考:【JS】async/await構文で書いたFetch APIからJSONデータを読み込む - クモのようにコツコツと
async/await
のエラーハンドリングはいろんな書き方があるが、よく使われるのはtry-catch
が多そう
※参考:async/awaitを利用したコードのエラーハンドリング - Qiita
※参考:try...catch - JavaScript | MDN
最後にhello
モジュールをエクスポート
export { hello };
レスポンスOK時の処理
try
の中にレスポンスOK時の処理を書いていく。
まずは変数を設定
const text: string = hello.message.text; const selector: string = hello.message.selector; const dom: HTMLButtonElement = document.querySelector(selector) as HTMLButtonElement;
- 変数
text
でテキストtext
を取得 - 変数
selector
でセレクタselector
を取得 - 変数
dom
でページ上のDOMを取得、引数はselector
TypeScriptの型定義についてはこちらを参照
※参考:【React】ピュアなTypeScriptモジュールを追加してみる(Reactとメタ言語の比較-6) - クモのようにコツコツと
処理を追加
dom.innerHTML = text;
console.log('text-> ' + text);
dom
のテキストをtext
にするtext
をコンソールに表示する
エラー時の表示を設定
catch
の中にエラー時の処理を追加
async function hello () { try { // 中略 } catch(err) { console.log('err!'); console.log(err); } };
catch()
を実行、引数はerr
- コンソールでテキスト「err!'」とエラー内容
err
を表示
「hello.json」のファイル名を「hello-.json」に打ち替えたところAPIエラーになってエラーがコンソールに表示された!
このままだとAPIエラーしかエラーにならない。値の不一致などもエラーにしたい。throw文を使うとユーザー定義のエラーを設定できそう。
throw 文は、ユーザー定義の例外を発生させます。現在の関数の実行は停止し (throw の後の文は実行されません)、コールスタック内の最初の catch ブロックに制御を移します。
async function hello () { try { // 中略 if (!text) { throw new Error('テキストが見つからないYo!'); } else if (!selector) { throw new Error('セレクタ名が見つからないYo!'); } else if (!dom) { throw new Error('DOMが見つからないYo!'); // 中略 } };
- もし
text
がなければ「テキストが見つからないYo!」エラー - もし
selector
がなければ「セレクタ名が見つからないYo!」エラー - もし
dom
がなければ「DOMが見つからないYo!」エラー
text
、selector
、dom
の値を打ち替えたりしたところ、ちゃんと上記のエラーがコンソールに表示された!
Innerコンポーネント
Inner()コンポーネントのテキスト
トップページに表示するInner()コンポーネントのテキストを設定する。「/data/data.json」に追記
"inner": [ { "title": "CSS(文字色)", "text": "CSSでタイトルの文字色変更。" }, { "title": "JS(文字列)", "text": "JSでテキストの文字列追加「<span class='hello'></span>」" } ],
Inner()コンポーネント
次にInner()
コンポーネントを作成する。「/components」フォルダに「Inner.tsx」を追加。
フックuseEffect
、hello
モジュール、data.json
をインポート
import React, { useEffect } from 'react'; import { hello } from '../modules/hello/hello'; import Data from '../data/data.json';
変数innerJson
でData.inner
を取得
const innerJson = Data.inner;
Inner()
コンポーネントを作成
// Component function Inner() { // 中略 }
フックuseEffect()
でDOM読み込み後にhello()
を実行
useEffect(() => { hello(); });
フックのライフサイクルについては下記を参照
※参考:【React】フックとjson-serverをFetch APIで連携(Reactライフサイクルの理解) - クモのようにコツコツと
JSX部分
return ( <> { innerJson.length >= 1 ? innerJson.map((innerJson, index) => <section key={ index }> <h2>{ innerJson.title }</h2> <p dangerouslySetInnerHTML={{ __html: innerJson.text }}></p> </section> ) : <section> <h2>内容が無いよう</h2> <p>へんじがない、ただのしかばねのようだ。</p> </section> } </> );
innerJson
の数が1以上だったら h2タグにtitle
、pタグにtext
を入れる処理をmap()
でループする。
さもなくばh2タグに「内容が無いよう」、pタグに「へんじがない、ただのしかばねのようだ。」と表示する
Reactスターターキットではmap()
のループのみだった。
※参考:react-app-started/Inner.tsx at main · ryo-i/react-app-started · GitHub
今回は三項演算子の分岐処理も追加した。制御構造(分岐やループ)はいろいろ試したがJSXの中で動くmap()
や参考演算子がやはり手軽だった。
※参考:【React】条件分岐の書き方(if文エラー回避、論理演算子、三項演算子) - クモのようにコツコツと
※参考:【React】ループの書き方(for文エラー回避、配列、map()) - クモのようにコツコツと
最後にInner
コンポーネントをエクスポート
export default Inner;
Profileコンポーネント
ProfileコンポーネントはAboutページで読み込む。こちらはベタガキのJSXを返す
// Component function Profile() { return ( <section> <h2>イイダリョウ(作者)</h2> <p>フロントエンドエンジニア。神奈川に住まう四十路のオジキ。 DTP→Webデザイナーから転向し今に至る。<br /> 引き続きコツコツの日々。ブログも書いてます。Webづくり やりたい時が 始め時!</p> <ul> <li><a href="https://twitter.com/idr_zz">Twitter</a></li> <li><a href="https://www.i-ryo.com">ブログ</a></li> <li><a href="https://qiita.com/i-ryo">Qiita</a></li> <li><a href="https://github.com/ryo-i">GitHub</a></li> </ul> </section> ); }
Profileコンポーネントをエクスポート
export default Profile;
最後に
ということで今回はコンポーネントとモジュールの設定部分をまとめました。今回得た知見
- fech APIのasync/await構文のおさらい
- try-catchを使ったエラーハンドリング
- throw文でAPIエラー以外のユーザー定義エラーも追加できる
次回はこれまで作ったコンポーネント使ってNext.jsのページファイルを作る部分をまとめます。
それではまた!
続き買いました!ページファイル編です♪
※参考:www.i-ryo.com
※参考:ReactでWebアプリを作るシリーズまとめ
qiita.com