20220112のReactに関する記事は4件です。

【まとめ】React(Next.js)の良さを語りたい

◆Reactとは ReactはFacebook社が開発しているJavaScriptのライブラリ リアクティブプログラミング Reactive: 反作用 何かの値が変化すると、それに反応して何かが起こります。 値の変化に反応して起こるのは表示の更新です。 リアクティブは値の伝播を中心にした考え方で、 元となる値を操作することでそれを利用する表示も自動的に更新されます。 ◆Next.jsとは Vercelというホスティングサービスを提供しているVercel社が開発しているReactのフレームワークです。 https://nextjs.org/ 公式サイトのファーストメッセージ The React Framework for Production をまさしく体現したような、プロダクトの本番環境で使える機能満載 & 設定が容易なフレームワークとなっています。 Pre Renderingが便利 事前にHTMLをレンダリングすることです。 事前にHTMLをレンダリングすることで、Googleのbotへコンテンツを正しく認識してもらえたり、スペックの高いサーバでレンダリングしてブラウザへの負荷を下げたりと、Client Side Renderingでの課題を解決できます。 =SEO対策 ルーティングなどのサポートが便利 ◆関数コンポーネントがオススメです 理由として、簡単に超ざっくり言うと コードが短く済む からです。(あとはReactチームがReactフックのサポートに結構力を入れているから) => https://zenn.dev/killit/articles/5af626acfb2ab7 React 初心者の難問、カスタムフック(Custom Hook) Q.npm startコマンドを実行した後、バックグラウンドで何が起こっていますか? webpack-dev-serverを使用して、通信可能な開発サーバーを起動します。 これにより、npm startによって実行されるスクリプトがアプリケーションディレクトリに直接配置されるため、Create-React-Appの動作を確認できます。さらに、これらのスクリプトで使用されるwebpack構成ファイルをディレクトリに追加します。最後に、package.jsonファイルを更新して、Create-React-Appが使用している依存関係を実際に確認できるようにします package.json を調べると そして、各コマンドが実際に行うことをたどることができる「スクリプト」セクションを確認してください。たとえば、「start」コマンドはここにあります。これは start.js を呼び出します スクリプトフォルダ内のファイル。これは、webpack-dev-serverが開始されるのを見るファイルです。 Q.ライフサイクル図ってなんぞ? 実装→更新  未実装 のときの流れ  4回目くらいでやっと理解…>< https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/ Q.理解せずにはいられない『props』『state』とは?? ◆props this.props には、このコンポーネントの呼び出し元によって定義された props が含まれています。 props の紹介は コンポーネントと props を見てください。 https://ja.reactjs.org/docs/components-and-props.html 関数コンポーネントがオススメです (コードが短く済む) ◆state state には、そのコンポーネント固有のデータが含まれており、これは時間の経過とともに変化する可能性があります。state はユーザ定義のものであり、プレーンな JavaScript オブジェクトでなければなりません。 レンダーやデータフローに値が使用されていない場合(たとえば、タイマー ID)は、値を state にする必要はありません。そのような値は、コンポーネントインスタンスのフィールドとして定義できます。 state の詳細については、state とライフサイクルを参照してください。 https://ja.reactjs.org/docs/state-and-lifecycle.html JSXは深く理解しておこう Reactといえば、このJSXが特徴的。 JSX では JavaScript のすべての能力を使うことができます。どのような JavaScript の式も JSX 内で中括弧に囲んで記入することができます。 Q.これを言われてピンときますか?? 親コンポーネントから子コンポーネントに「props を渡す」こと 過去書いたReactのQiita記事はこちら
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JestでReactのスナップショットテストするとexpect(tree).toMatchSnapshot()の行でエラーが発生する

背景 Reactアプリのレンダリングが正常であるかをテストする場合、Jestのスナップショット機能を使用する方法がある。 今回、業務で単体テストとしてスナップショット機能を使用したのだが、テストを実行すると「expect(tree).toMatchSnapshot()」の箇所に波線が引かれ、エラーメッセージが出力されたのでその原因と解決策をまとめた。 開発言語 (バージョン情報) React.js (17.0.2) Next.js (11.1.2) TypeScript (4.4.3) Jest (27.2.2) 試したこと src/pages/Page1/index.tsxのテストファイルとして、「src/pages/Page1/index.spec.tsx」を作成する。 そして下記のコードを書く。 index.spec.tsx import TestRenderer from 'react-test-renderer'; import Component1 from 'components/organisms/Component1'; import interfaceA from 'interfaces'; describe('Snapshots 1', () => { test('pattern 1'), () => { const sampleDatas: interfaceA[] = [ { sampleId: '1', sampleName: 'Sample001', comment: 'test test', }, ]; const onClickPrepare = () = > { // 処理 }; const onClickApply = () = > { // 処理 }; const tree = TestRenderer.create( <Component1 datas={sampleDatas} onClickPrepare={onClickPrepare} onClickApply={onClickApply} />, ).toJSON(); expect(tree).toMatchSnapshot(); )}; }) ターミナルにてコマンドを実行する。 jest pages/Page1/index.spec.tsx 下記メッセージのエラーが出力される。 1 snapshots obsolete from 1 test suite. To remove them all, run `npm test -- -u`. 原因 直前に別のコンポーネントのスナップショットを撮ったにも関わらず、今回「-u」オプションで実行しなかったため。 「-u」オプションはスナップショットを強制的に更新するオプションで、別コンポーネントのスナップショットを撮るときは一度「-u」オプションで実行する必要がある。 解決策 スナップショットを撮る対象のコンポーネントを切り替えた直後など、一回目の実行時は下記コマンドで実行する。 jest -u pages/Page1/index.spec.tsx まとめ スナップショットでテストするとき、初回のみ「-u」オプションでコマンド実行する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自社LP開発でNext.jsのSGを選択した話

プロダクトLPをmicroCMSでヘッドレス化しようとした 以下の記事で以前既存のプロダクトLPをヘッドレス化する計画について書きました。 今回はそのヘッドレスCMS化したLPの公開にあたり、Next.js側の設定でどのレンダリング方法を選択するのが最適なのかについての記事になります。 レンダリング方法の選定 レンダリング方法はCSR/SSR/SG/ISRの4種類がありますが、LPという特性上SEOと表示速度に焦点を当てて選定をしました。 CSR 通常のSPAです。クライアント側でデータフェッチおよびレンダリングを行います。 メリット リアルタイムにデータが反映がされる #### デメリット SEOに弱い(クローラによる) データが反映されていないページが最初に一瞬表示される #### 結果 特に理由がない限りCSRで基本はいいのかなと思っているのですが、SEOの点で引っかかるので今回は無しとしました。 ## SSR サーバーサイドでデータフェッチおよびレンダリングを行う方法です。 #### メリット クライアント側のスペックに左右されず、高速でレンダリングできる リアルタイムにデータが反映がされる SEOに強い #### デメリット 遷移にラグを感じる(サーバーサイドでレンダリングが完了するのを待つため) #### 実装方法 getServerSidePropsを使って実装します。 export async function getServerSideProps(context) {     // サーバーサイドで実施するデータフェッチなどの処理を書きます return { props: {}, // ページコンポーネントにpropsとして渡されます } } 結果 SEOに強いのは嬉しいのですが、遷移のラグが気になります...。選択肢の1つとしてはありです。 SG ビルド時にあらかじめ静的ページを生成し、リクエスト時はそのページを返すだけという方法。 メリット 表示速度が速い #### デメリット データの更新が即時反映されない #### 実装方法 getStaticPropsを使って実装します。 export async function getStaticProps(context) { return { props: {}, // ページコンポーネントにpropsとして渡されます } } Dynamic Routingの場合は別途getStaticPathsを設定する必要があります。これによりどのページをビルド時に生成するか決めることができます。 // この関数はサーバーサイドのビルド時に呼び出されます // ここで指定していないパスに対してリクエストがあった場合はSSRもしくはCSRによりページが生成されます export async function getStaticPaths() { const res = await fetch('https://.../posts') const posts = await res.json() const paths = posts.map((post) => ({ params: { id: post.id }, })) // ここに指定したパスのみビルド時にプリレンダリングします // { fallback: blocking } と記述すると、指定していないパスに対してリクエストがあった場合SSRが実行されキャッシュが生成されます return { paths, fallback: 'blocking' } } 結果 表示速度が早くSEOも強いのでできればこれが良い...! ただデータの更新が(ビルドしないと)反映されないというのがネックだなと。完全静的ページしか使えない気がします。 定期実行でビルドさせたり、Webhookで記事更新タイミングでビルドが走るのならありですね。 おまけ SSGって一昔前の呼び方らしいです。おじさんみ?‍?を出さないためにも僕はSGって読んでます。頑張ってます。 ISR SGの機能に加えて定期的にデータを更新して裏で新たなページを生成します。SGの欠点を補うレンダリング方法になります。 メリット キャッシュにより2回目以降の表示速度が爆速 #### デメリット 初回アクセス(つまりキャッシュがない場合)はSSR(またはCSR)になる 定期的なデータ更新により新しいキャッシュ生成後の1回目のリクエストでは古いキャッシュが返される #### 実装方法 returnする値に、 revalidateを追加するだけです。キャッシュの再生成をする秒数になります。 getStaticPathsでpathsを指定してしまうと、そのページは初回アクセス時にビルド時の情報が表示され続けてしまうことになるので、ISR時はここを[]にしておく必要がありそうです。 export async function getStaticProps() { const res = await fetch('https://.../posts') const posts = await res.json() return { props: { posts, }, // 10秒ごとにキャッシュを更新 revalidate: 10, // 秒数を指定 } } export async function getStaticPaths() { return { paths: [], fallback: 'blocking', }; }; 結果 実装の手間もかからず、すごく便利な機能を備えています。 ただ、初回アクセス時のSSR特有の遷移ラグは気になりました。 Zennでも一部のページでISRを採用しているとのこと。 ユーザーが投稿するシステムなので、投稿頻度もかなり多いと思いますしそういった特性のものはISRのほうが良さそう。 選ばれたのはSGでした 色々悩みましたが、記事投稿頻度などを考慮してSGを選択しました。 microCMSのWebhookを使用して記事更新時にビルドする やはり何とかしてSGで実現したいと考えていたところ、microCMSのWebhookを使用する方法に行き着きました。 Github Actionsでビルドの定期実行をすることも考えていましたが、一番反映が早く確実なのはこの方法かなという結論に至りました。 LP内記事などはユーザーが任意で投稿するサービスとは違って投稿頻度は極めて低いと言えるので、投稿ごとにビルドするのは問題ないと考えています。 設定方法も簡単で、Vercel, microCMSを少しいじるだけで設定が完了するのですごく楽でした。 めでたしめでたし。 その他の参考記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Next.jsまたはReact.js×CustomHooks×css-moduleの新しいフォルダ構成について検討してみた。

現状のファイル・フォルダ構成 現状こんな感じの構成を...(フォルダの種類や構成は少し省略しています。Next.jsのプロジェクトです。) src │ ├─atoms//atomsに関連するファイルもののみを入れるフォルダ │ ├─hooks//atomで使用するカスタムフック │ │ ├─useAtomA.ts │ │ ├─useAtomB.ts │ │ └─useAtomC.ts │ └─styles//atomで使用するcss-module │ │ ├─atomA.module.scss │ │ ├─atomB.module.scss │ │ └─atomC.module.scss │ ├─AtomA.tsx │ ├─AtomB.tsx │ └─AtomC.tsx │ ├─molecules//moleculesに関連するもののみを入れるフォルダ │ ├─hooks//moleculeで使用するカスタムフック │ │ ├─useMoleculeA.ts │ │ ├─useMoleculeB.ts │ │ └─useMoleculeC.ts │ └─styles//moleculeで使用するcss-module │ │ ├─moleculeA.module.scss │ │ ├─moleculeB.module.scss │ │ └─moleculeC.module.scss │ ├─MoleculeA.tsx │ ├─MoleculeB.tsx │ └─MoleculeC.tsx │ ├─organisms//organismsに関連するもののみを入れるフォルダ │ ├─hooks//organismsで使用するカスタムフック │ │ ├─useOrganismA.ts │ │ ├─useOrganismB.ts │ │ └─useOrganismC.ts │ └─styles//organismsで使用するcss-module │ │ ├─organismA.module.scss │ │ ├─organismB.module.scss │ │ └─organismC.module.scss │ ├─OrganismA.tsx │ ├─OrganismB.tsx │ └─OrganismC.tsx │ ├─pages │ └─index.tsx ├─hooks//pages内コンポーネントで使用するカスタムフック │ └─useIndex.ts ├─styles//pages内コンポーネントで使用するcss-module │ └─index.module.scss │ └─utils//共有・共通する処理・カスタムフック・スタイル・定数等入れる 改善案 現在のプロジェクトのコンポーネント数は約50個。 ここで、共有するカスタムフックやcss-moduleよりも、一つのコンポーネントにしか使われないカスタムフックやcss-moduleの方が圧倒的に多い。 どのカスタムフックやcss-moduleがどのコンポーネントのものなのか特定するのがめんどくさく、上記の理由もあり、一つのコンポーネントにしか使われていないカスタムフックとcss-moduleの命名は、 カスタムフック名:use{コンポーネント名} css-module名:{先頭を小文字にしたコンポーネント}.module.scss としていた。 そこで、大抵カスタムフック・css-moduleとコンポーネントは上記の現状のファイル・フォルダ構成のように、1対1の関係として見れるのではと考えた。 新しいファイル・フォルダ構成 atoms、molecules、organismsで絞るのではなく、さらに一コンポーネントで絞ってフォルダを作成しコンポーネント、カスタムフック、css-moduleをその中に作成する。 (変更部分のみ抜粋) src │ ├─atoms │ ├─AtomA//AtomAコンポーネントに関連するファイルのみを入れるフォルダ │ │ ├─AtomA.tsx │ │ ├─useAtomA.ts │ │ └─atomA.module.scss │ ├─AtomB//AtomBコンポーネントに関連するファイルのみを入れるフォルダ │ │ ├─AtomB.tsx │ │ ├─useAtomB.ts │ │ └─atomB.module.scss │ └─AtomC//AtomCコンポーネントに関連するファイルのみを入れるフォルダ │ │ ├─AtomC.tsx │ │ ├─useAtomC.ts │ │ └─atomC.module.scss │ ├─molecules │ ├─MoleculeA//MoleculeAコンポーネントに関連するファイルのみを入れるフォルダ │ │ ├─MoleculeA.tsx │ │ ├─useMoleculeA.ts │ │ └─atomA.module.scss │ ├─MoleculeB//MoleculeBコンポーネントに関連するファイルのみを入れるフォルダ │ │ ├─MoleculeB.tsx │ │ ├─useMoleculeB.ts │ │ └─atomB.module.scss │ └─MoleculeC//MoleculeCコンポーネントに関連するファイルのみを入れるフォルダ │ │ ├─MoleculeC.tsx │ │ ├─useMoleculeC.ts │ │ └─moleculeC.module.scss │ ├─organisms │ ├─OrganismA//OrganismAコンポーネントに関連するファイルのみを入れるフォルダ │ │ ├─OrganismA.tsx │ │ ├─useOrganismA.ts │ │ └─organismA.module.scss │ ├─OrganismB//OrganismBコンポーネントに関連するファイルのみを入れるフォルダ │ │ ├─OrganismB.tsx │ │ ├─useOrganismB.ts │ │ └─organismB.module.scss │ └─OrganismC//OrganismCコンポーネントに関連するファイルのみを入れるフォルダ │ │ ├─OrganismC.tsx │ │ ├─useOrganismC.ts │ │ └─organismC.module.scss それぞれのカスタムフックとcss-moduleは同じフォルダ内のコンポーネントからしか使用されないので、カスタムフックとcss-moduleは以下のように名称も共通にするのも良いのでは。 src │ ├─atoms │ ├─AtomA │ │ ├─AtomA.tsx │ │ ├─hook.ts │ │ └─style.module.scss │ ├─AtomB │ │ ├─AtomB.tsx │ │ ├─hook.ts │ │ └─style.module.scss │ └─AtomC │ │ ├─AtomC.tsx │ │ ├─hook.ts │ │ └─style.module.scss │ ├─molecules │ ├─MoleculeA │ │ ├─MoleculeA.tsx │ │ ├─hook.ts │ │ └─style.module.scss │ ├─MoleculeB │ │ ├─MoleculeB.tsx │ │ ├─hook.ts │ │ └─style.module.scss │ └─MoleculeC │ │ ├─MoleculeC.tsx │ │ ├─hook.ts │ │ └─style.module.scss │ ├─organisms │ ├─OrganismA │ │ ├─OrganismA.tsx │ │ ├─hook.ts │ │ └─style.module.scss │ ├─OrganismB │ │ ├─OrganismB.tsx │ │ ├─hook.ts │ │ └─style.module.scss │ └─OrganismC │ │ ├─OrganismC.tsx │ │ ├─hook.ts │ │ └─style.module.scss 共に勉強する友達が新しくプロジェクトを作成したのでこのフォルダ構成を紹介すると気に入ってくれて試してくれることに、、、(1月前くらいかなぁ) そして先日、冬休みが明け学校に向かう際、沢山の問題に気付く。 良くない点 そもそも、atomとmoleculeはロジックを持たないことが多いため、カスタムフックを使用しない。 できるだけatomやmoleculeには状態やロジックを持たせないように設計するため、カスタムフックを使用しない。 小さく独立性の高いコンポーネントは、スタイルも親に決めてもらうように設計するため、css-moduleを使用しないことが多い。 organismsもロジックやスタイルがないこともあるため、カスタムフックやcss-moduleを使用しないことがある。 -ロジックが多いコンポーネントはカスタムフックが複数になることがあるため、カスタムフックのtsファイル名を「hook」にするなどで共通化は厳しい。依存したstateがなにか、なにをするhookなのかを命名したほうがよさそう。 以上の問題により、実際はそれぞれのコンポーネントのフォルダ内には、 コンポーネントとカスタムフックとcss-module コンポーネントとカスタムフック コンポーネントとcss-module コンポーネントのみのためフォルダから出すorコンポーネントのみ カスタムフックが複数あるフォルダと存在しないフォルダ とフォルダ内ファイル数が毎回変わり少し煩雑になり、見にくいし扱いずらい気がする。↓ Aパターン(フォルダ内がコンポーネントのみの場合も変わらずフォルダ内に格納) src │ ├─atoms │ ├─AtomA │ │ └─AtomA.tsx │ ├─AtomB │ │ ├─AtomB.tsx │ │ └─style.module.scss │ └─AtomC │ │ └─AtomC.tsx │ ├─molecules │ ├─MoleculeA │ │ ├─MoleculeA.tsx │ ├─MoleculeB │ │ └─MoleculeB.tsx │ └─MoleculeC │ │ ├─MoleculeC.tsx │ │ └─style.module.scss │ ├─organisms │ ├─OrganismA │ │ ├─OrganismA.tsx │ │ └─style.module.scss │ ├─OrganismB │ │ ├─OrganismB.tsx │ │ ├─hook.ts │ │ └─style.module.scss │ └─OrganismC │ │ ├─OrganismC.tsx │ │ ├─hook.ts │ │ └─style.module.scss Bパターン(フォルダ内がコンポーネントのみの時、コンポーネントを外に出した場合) src │ ├─atoms │ ├─AtomA │ │ └─AtomA.tsx │ ├─AtomB │ │ ├─AtomB.tsx │ │ └─style.module.scss │ └─AtomC.tsx │ ├─molecules │ ├─MoleculeA │ │ ├─MoleculeA.tsx │ ├─MoleculeB.tsx │ └─MoleculeC │ │ ├─MoleculeC.tsx │ │ └─style.module.scss │ ├─organisms │ ├─OrganismA │ │ ├─OrganismA.tsx │ │ └─style.module.scss │ ├─OrganismB │ │ ├─OrganismB.tsx │ │ ├─hook.ts │ │ └─style.module.scss │ └─OrganismC │ │ ├─OrganismC.tsx │ │ ├─hook.ts │ │ └─style.module.scss 個人的にはAパターンの方が、「コンポーネントのためのフォルダを作成する」といったルール化がされているので、import文を書く際に分かりやすいのでこちらを推す。 また、例えばatomでもInput要素を扱うものなどでフォルダ分けしたい時もありその場合さらに一段と階層が深くなる。このフォルダ構成を貫くのが難しい。柔軟性が無い。 感想 vscodeでこの構成を見たとき、フォルダまみれでなんか引く...。問題点を友達に報告した際、このままでは理想と違った見た目や使用感になることを嫌がり今回はいつも通りのフォルダ構成に途中変更しそうな様子。 実際に開発を進めていてメリットを感じなかったらしい。また、コンポーネント数10数個ですでにフォルダが多くて扱いにくく混乱していたらしい。 問題について、なぜ早く気付かなかったんだと言われてその通りだと思った。でも実際にこの構成を実践しているあなたこそなぜ気づかなかったんだと言おうと思ったが、頭を抱えていたので言えなかった。なんかごめん。 そもそも一対一の関係のカスタムフックとcss-moduleの命名は、 カスタムフック名:use{コンポーネント名} css-module名:{先頭を小文字にしたコンポーネント}.module.scss としていれるだけで十分わかりやすいので今のままで良かったのかなと思う。 ただ、自分は開発をする際に、css-moduleとカスタムフックが、コンポーネントに対して1体1になりがちなので、着想はよかったと思う。現状に不満ありますし、現状よりもっと良い方法ありそうと考えているのでまた次回あります...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む