20210801のReactに関する記事は12件です。

create react appでyarn startでコケたときの解決法

記事の定義 yarn start をすると下記のエラーが出てきました。 この記事では上記の解決法を記載する。 error An unexpected error occurred: "/Users/hoge/hogehoge/package.json: Unexpected string in JSON at position 185". 起きたこと&状況 ソースコード pakege.json { "name": "hoge", "version": "0.1.0", "private": true, "dependencies": { "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "4.0.3" } "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, } バージョン node -v v14.17.4 yarn -v 1.22.10 解決方法 Scripts の前の } の後ろに , を追加する。 そしてScripts の } の , を削除する。 pakege.json { "name": "hoge", "version": "0.1.0", "private": true, "dependencies": { "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "4.0.3" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" } } 余談 おそらく文字列("hoge")が続く場合は , で接続する必要性がある
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Server Components を Amazon Linux 2 で試す

React server Components (デモ)を Amazon Linux2 で動かす手順です。 本家 - React blog https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html - GitHub https://github.com/reactjs/server-components-demo 原則、上記のREADMEに従っています。 環境 Amazon Linux 2 Amazon Linux 2 AMI (HVM), SSD Volume Type - ami-09ebacdc178ae23b7 (64 ビット x86) / ami-06b31a9cee8dfac33 (64 ビット Arm) EC2(例では ec2-54-95-41-241.ap-northeast-1.compute.amazonaws.com とします) セキュリティ(デモでは4000番ポートを使用するので、ポートを開けておきます) セキュリティルール カスタムTCPルール TCP 4000 0.0.0.0/0 カスタムTCPルール TCP 4000 ::/0 ターミナルからsshでつなぎます。 server-components-demo のインストール 前準備:アップデート $ yum update -y nodeのインストール(Amazon Linux 2 の場合) $ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash $ . ~/.nvm/nvm.sh $ nvm install node $ node -e "console.log('Running Node.js ' + process.version)" gitのインストール $ yum install git -y リポジトリから server-components-demo を入手します $ git clone https://github.com/reactjs/server-components-demo.git server-components-demo の初期設定 $ cd server-components-demo/ $ npm install $ npm start 下記のメッセージが表示されたら、Webブラウザで表示してみます。設定が不足しているので、現時点ではエラーになりますが、確認のために見てみましょう。 [0] [nodemon] starting `node --conditions=react-server server` [0] React Notes listening at 4000... [1] Finished running webpack. [1] [nodemon] clean exit - waiting for changes before restart URL Webブラウザで確認(ポートは4000番) http://ec2-54-95-41-241.ap-northeast-1.compute.amazonaws.com:4000/ ※現時点ではエラーメッセージが表示されます。 Application Error Error: connect ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1142:16) ターミナルに戻り、 Control + C で終了します。 テストデータを投入 READMEに従って、テストデータを入れていきます(dockerを使う方法です) dockerのインストール(Amazon Linux 2 の場合) $ amazon-linux-extras install docker $ chkconfig docker on $ systemctl enable docker.service $ systemctl restart docker.service docker-composeのインストール $ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose $ chmod +x /usr/local/bin/docker-compose dockerの起動とテストデータの投入(必ず server-components-demo ディレクトリの中で実行してください) $ docker-compose up -d $ docker-compose exec notes-app npm run seed もういちど、Webブラウザから確認します。 http://ec2-54-95-41-241.ap-northeast-1.compute.amazonaws.com:4000/ 今度はエラーにならずに、データが入った状態で表示されています。 試してみる面白いこと READMEからの引用(Google翻訳)です。 サイドバーのメモにカーソルを合わせ、展開/折りたたみの切り替えをクリックして、メモを展開します。次に、メモを作成または削除します。展開されたノートはどうなりますか? 編集中にノートのタイトルを変更し、既存のアイテムの編集がサイドバーでどのようにアニメーション化されるかに注目してください。リストの途中でメモを編集するとどうなりますか? 任意のタイトルを検索します。検索入力に検索テキストが残っている状態で、検索テキストと一致するタイトルの新しいメモを作成します。何が起こるのですか? Slow 3Gで検索し、インラインローディングインジケーターを確認します。 2つの音符を前後に切り替えます。次回それらを再び切り替えるときに、新しい応答を送信しないことに注意してください。 fetch(http://localhost:4000/sleep/....) 通話のコメントを解除するNote.server.jsかNoteList.server.js、人為的な遅延を導入してサスペンスをトリガーします。 コメントを外すだけの場合はNote.server.js、メモを開くたびにフォールバックが表示されます。 コメントを外すだけの場合はNoteList.server.js、最初のページの読み込み時にリストのフォールバックが表示されます。 両方でコメントを外すと、両方が応答するまで表示する新しいものがないため、あまり面白くありません。 新しいサーバーコンポーネントを追加し、の検索バーの上に配置しApp.server.jsます。dbからインポートしdb.serverて使用db.query()し、ノートの数を取得します。メモを追加または削除するとどうなるかを観察します。 ソースコードの場所をもういちど記載します。 GitHub https://github.com/reactjs/server-components-demo
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Server Components (demo) を Amazon Linux 2 で試す (Docker編)

React server Components (デモ)を Amazon Linux2 で動かす手順です。 本家 - React blog https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html - GitHub https://github.com/reactjs/server-components-demo 原則、上記のREADMEに従っています。 環境 Amazon Linux 2 Amazon Linux 2 AMI (HVM), SSD Volume Type - ami-09ebacdc178ae23b7 (64 ビット x86) / ami-06b31a9cee8dfac33 (64 ビット Arm) EC2(例では ec2-54-95-41-241.ap-northeast-1.compute.amazonaws.com とします) セキュリティ(デモでは4000番ポートを使用するので、ポートを開けておきます) セキュリティルール カスタムTCPルール TCP 4000 0.0.0.0/0 カスタムTCPルール TCP 4000 ::/0 ターミナルからsshでつなぎます。 server-components-demo のインストール 前準備:アップデート $ yum update -y nodeのインストール(Amazon Linux 2 の場合) $ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash $ . ~/.nvm/nvm.sh $ nvm install node $ node -e "console.log('Running Node.js ' + process.version)" gitのインストール $ yum install git -y リポジトリから server-components-demo を入手します $ git clone https://github.com/reactjs/server-components-demo.git server-components-demo の初期設定 $ cd server-components-demo/ $ npm install $ npm start 下記のメッセージが表示されたら、Webブラウザで表示してみます。設定が不足しているので、現時点ではエラーになりますが、確認のために見てみましょう。 [0] [nodemon] starting `node --conditions=react-server server` [0] React Notes listening at 4000... [1] Finished running webpack. [1] [nodemon] clean exit - waiting for changes before restart URL Webブラウザで確認(ポートは4000番) http://ec2-54-95-41-241.ap-northeast-1.compute.amazonaws.com:4000/ ※現時点ではエラーメッセージが表示されます。 Application Error Error: connect ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1142:16) ターミナルに戻り、 Control + C で終了します。 テストデータを投入 READMEに従って、テストデータを入れていきます(Dockerを使う方法です) Dockerのインストール(Amazon Linux 2 の場合) $ amazon-linux-extras install docker $ chkconfig docker on $ systemctl enable docker.service $ systemctl restart docker.service docker-composeのインストール $ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose $ chmod +x /usr/local/bin/docker-compose Dockerの起動とテストデータの投入(必ず server-components-demo ディレクトリの中で実行してください) $ docker-compose up -d $ docker-compose exec notes-app npm run seed もういちど、Webブラウザから確認します。 http://ec2-54-95-41-241.ap-northeast-1.compute.amazonaws.com:4000/ 今度はエラーにならずに、データが入った状態で表示されています。 試してみる面白いこと READMEからの引用(Google翻訳)です。 サイドバーのメモにカーソルを合わせ、展開/折りたたみの切り替えをクリックして、メモを展開します。次に、メモを作成または削除します。展開されたノートはどうなりますか? 編集中にノートのタイトルを変更し、既存のアイテムの編集がサイドバーでどのようにアニメーション化されるかに注目してください。リストの途中でメモを編集するとどうなりますか? 任意のタイトルを検索します。検索入力に検索テキストが残っている状態で、検索テキストと一致するタイトルの新しいメモを作成します。何が起こるのですか? Slow 3Gで検索し、インラインローディングインジケーターを確認します。 2つの音符を前後に切り替えます。次回それらを再び切り替えるときに、新しい応答を送信しないことに注意してください。 fetch(http://localhost:4000/sleep/....) 通話のコメントを解除するNote.server.jsかNoteList.server.js、人為的な遅延を導入してサスペンスをトリガーします。 コメントを外すだけの場合はNote.server.js、メモを開くたびにフォールバックが表示されます。 コメントを外すだけの場合はNoteList.server.js、最初のページの読み込み時にリストのフォールバックが表示されます。 両方でコメントを外すと、両方が応答するまで表示する新しいものがないため、あまり面白くありません。 新しいサーバーコンポーネントを追加し、の検索バーの上に配置しApp.server.jsます。dbからインポートしdb.serverて使用db.query()し、ノートの数を取得します。メモを追加または削除するとどうなるかを観察します。 ソースコードの場所をもういちど記載します。 GitHub https://github.com/reactjs/server-components-demo
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ゼロから始めるChakraUIカスタムグローバル設定

ChakraUIをNextJSのサイトで使い始めてグローバル設定したのでまとめてみました。 現在絶賛、制作中のブログでお試しで導入したところ気に入っています。 ChakraUI とは TailWindCSS にインスパイアされつつ styled-system 、 emotion や framer-motion をベースにして作られた Component Library です。 便利なChakraUI制のHooksなんかも用意されています。 使ってみた初日の感触としては TailWindCSS をもっとシンプルにしたスタイルのライブラリという印象を受けました。 今回はグローバル設定のところまで解説。 ※この記事ではReact上でのChakraUI使用を想定しています ChakraUIをインストールする npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4 or yarn add @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4 emotion や framer-motion を使っているため一緒にインストール。 できるだけ親側でChakraProviderコンポーネントを設定する ChakraProviderで囲われた親以下のコンポーネントに対して使えるようになります。 自分の場合はNextJSの初期化ページ(initialize pages)にあたる _app.tsx のコンポーネントで囲いました。 _app.tsx import { ChakraProvider } from "@chakra-ui/react" export default function App({ Component, pageProps }) { return ( <> <ChakraProvider theme={theme}> <Component {...pageProps} /> </ChakraProvider> </> ); } Boxコンポーネントがdivタグ代わりになる これでChakraProviderより以下のところでChakraUIが使えるようになったので使っていきたいと思います。 詳細は割愛しますがBoxコンポーネントがdivタグのような役割となります。 hogeComponent.tsx import { FC } from 'react' import { Box, Text, Flex } from '@chakra-ui/react' const HogeComponent: FC<{sitetitle: string, subtitle: string}> = ({sitetitle, subtitle}) => { return ( <Flex justifyContent="center"> <Box borderBottom="main" w="288px" p="8px 0"> <Text fontSize="big">{siteTitle}<Text as="span" fontSize="12px" display="block">{subTitle}</Text></Text> </Box> </Flex> ); }; export default HogeComponent; TailWind系のライブラリを触ったことがある人ならだいたい雰囲気がつかめるかなと。 marginはm、paddingはp、widthはw、heightはhのように省略形で記載できるプロパティもあります。 Boxコンポーネントはdiv、Texコンポーネントはp、Flexコンポーネントはdisplay: flexのdivのような粒度からもっと大きな粒度でコンポーネントが色々と用意されています。 デフォルトでChakraUIのCSSresetが当たっている resetCSSに関してはChakraProviderを囲うことでスタイルが当たってくれます。(オプションでfalseにすることもできる) 本題:グローバル設定をカスタマイズする さて、準備ができたためここで本題のグローバル設定を行なっていきます。自分が使っている設定をコピペしました。 やり方としてはthemeの中身を設定したらChakraProviderにtheme引数を渡せばOKです。 _app.tsx import { ChakraProvider, extendTheme } from "@chakra-ui/react" const theme = extendTheme({ colors: { moji: { main: "#030D1B", sub: "#A09E9F" }, solid: { main: "#DEDCDB", }, brand: { main: "#4B95C2", mark: "#D8B189", }, }, fontSizes: { xmini: '0.7em', mini: '0.8em', base: '16px', big: '1.2em', }, borders: { main: "1px solid #DEDCDB" }, styles: { global: { "html, body": { color: "moji.main", background: "#fff", lineHeight: "1.4142135623", fontSize: "base", letterSpacing: "0.08em", textAlign: "justify", textJustify: "inter-ideograph", fontFamily: '"Helvetica Neue", Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", "BIZ UDPGothic",Meiryo, sans-serif', }, a: { color: "brand.main", }, } } }) export default function App({ Component, pageProps }) { return ( <> <ChakraProvider theme={theme}> <Component {...pageProps} /> </ChakraProvider> </> ); } のような形で設定した内容を渡してあげました。 単純に以下のglobalプロパティに好きな値を入れていけばスタイルが全体に当たってくれます。 キャメルケース式の書き方です(styled-componentやemotionなどを使ったことがある人ならわかるかと。素のcssはスネークケースですがだいたいそのままキャメルケース式に置き換えれば動きます。) _app.tsx 省略 styles: { global: { "html, body": { color: "moji.main", background: "#fff", lineHeight: "1.4142135623", fontSize: "base", letterSpacing: "0.08em", textAlign: "justify", textJustify: "inter-ideograph", fontFamily: '"Helvetica Neue", Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", "BIZ UDPGothic",Meiryo, sans-serif', }, a: { color: "brand.main", }, } } 省略 ちょっとよくわからないところがあるぞ? color: "moji.main" fontSize: "base" 上は素のcssの値としては存在しないものですが、 _app.tsx 省略 colors: { moji: { main: "#030D1B", sub: "#A09E9F" }, solid: { main: "#DEDCDB", }, brand: { main: "#4B95C2", mark: "#D8B189", }, }, fontSizes: { xmini: '0.7em', mini: '0.8em', base: '16px', big: '1.2em', }, borders: { main: "1px solid #DEDCDB" }, 省略 で設定したものです。その場で引っ張ることができるためglobalプロパティ側でも使っています。 テーマプロパティ: { 任意のプロパティ }, といった形でカスタム設定していきます。 ただし、デフォルトで設定されているキーは使えません。(例えばテーマプロパティfontSizeの 'xs' や 'sm' など) ドキュメントの以下のページにも"テーマプロパティ"が載っているのでそれらをキーとして追加カスタマイズしていくことができます。 ChakraUIがオプションで用意しているテーマ カスタマイズせずともChakraUI側が用意しているテーマもあります(詳細は割愛) テーマプロパティに追加したプロパティは fontSize="big" のような形でスタイルを当てていくことができます。 hogeComponent.tsx import { FC } from 'react' import { Box, Text, Flex } from '@chakra-ui/react' const HogeComponent: FC<{sitetitle: string, subtitle: string}> = ({sitetitle, subtitle}) => { return ( <Flex justifyContent="center"> <Box borderBottom="main" w="288px" p="8px 0"> // fontSize="big"はカスタムで追加したプロパティのキー <Text fontSize="big">{siteTitle}<Text as="span" fontSize="12px" display="block">{subTitle}</Text></Text> </Box> </Flex> ); }; export default HogeComponent; 少し気になったところ 公式ドキュメントのボリュームが小さいのでもう少し詳細な説明など欲しかったなあと。 あとテーマプロパティ一覧のページとか欲しいかも。 参考になった記事 課金(今は200円)になってしまうのですが下の記事のまとめがとても助かっています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】再レンダリングとメモ化

はじめに Reactの再レンダリングとメモ化、について学習したことを簡単にまとめてみました。 参考 わかりやすく有益な記事で、参考にさせていただきした。 本当にありがとうございました!! https://qiita.com/soarflat/items/b9d3d17b8ab1f5dbfed2#usecallback-%E3%82%92%E9%96%A2%E6%95%B0%E3%81%AE%E5%86%8D%E7%94%9F%E6%88%90%E3%82%92%E9%98%B2%E3%81%90%E7%9B%AE%E7%9A%84%E3%81%A7%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6%E3%81%AF%E3%81%84%E3%81%91%E3%81%AA%E3%81%84%E3%81%AE%E3%81%8B https://zenn.dev/b1essk/articles/react-re-rendering コンポーネントの再レンダリングが起きる条件 1. stateが更新されたとき 例:フォーム入力の値を監視するstateを保持しており、そのstateの値に変更があったとき(フォームに値を入力 or 削除するとき) 2. propsが更新されたとき 例:親コンポーネントで値を保持しており、その値をpropsで子コンポーネントに渡して表示している際にその値に変更があったとき 3. 親コンポーネントが再レンダリングされるとき メモ化による再レンダリングの最適化 再レンダリングの最適化とは Reactのコンポーネントではレンダリングは頻繁に起きることが多いため、そのたびに重い処理などを走らせるとパフォーマンスが落ちる。 その不必要な処理をできるだけ抑え、処理は必要最低限にしたい。 それによりページ描画スピード向上など、パフォーマンスの最適化をはかることが再レンダリングの最適化である。 メモ化とは 計算処理結果や生成された関数、コンポーネントなどを保持しておき、それを適切に再利用すること。 メモ化しておくことで不必要に処理を走らせることを防ぐことができる。 ※メモ化すること事態にもコストが発生するようなので、何でもかんでもメモ化すれば良いということでもないようである。 メモ化の方法 memo() コンポーネントに対してのメモ化 useCallback() コールバック関数に対してのメモ化 useMemo() 値に対してのメモ化 memo() コンポーネントに対してのメモ化 親コンポーネントが再レンダリングされても、子コンポーネントに渡すpropsの値が変更されない限り、子コンポーネントは再レンダリングされないようにする仕組み。 逆に言えばmemo()を利用してもpropsに変更があれば際レンダリングされてしまう。 memo(コンポーネント); useCallback() コールバック関数に対してのメモ化 不必要に関数が再生成されることを防ぐ。 第2引数で指定する依存配列に変更がない限り第1引数のコールバック関数は再生成されず、メモ化された保持している関数をコールバック関数を返す。 useCallbackを利用しないと、このコンポーネントがレンダリングされるたびにその関数は再生成される。この関数をpropsとして渡していたら、再生成のタイミングでpropsが変更されたことを子コンポーネントが検知して子コンポーネントも再レンダリングされる。 const 関数名 = useCallback(コールバック関数, [依存配列]); ※ 利用方針は様々あると思うが、基本的にはmemo(コンポーネント)とセットで利用することで子コンポーネントの不必要な再レンダリングを防ぐ目的で利用する。 useMemo() 値に対してのメモ化 不必要に重い処理を走らせることを防ぐ。 第2引数で指定する依存配列に変更がない限り第1引数の処理は実行されず、メモ化された保持している値を返す。 const 変数名 = useMemo(処理, [依存配列]); ※第1引数で渡す処理が重い処理でなければ、useMemo()を利用するコストの方が大きくなることもあり、利用するメリットが無いこともあるよう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者による初心者のためのGatsbyJS覚書2

この記事について 会社の業務でGatsbyJSを少しだけさわった経験がある新卒2年目が作成しています。 参考資料として書籍を使用していますが、筆者がビギナークラスのため読んでいて「?」となる部分や間違っている箇所もあるかと思います。 参考までに、そして間違えている箇所がありましたらご連絡いただけると嬉しいです。 Chapter1:ページの作成の備忘録メモ 知っている方からすると「当たり前だろ」となるかもですが、、、 個人的に「なるほど…」「覚えておこう!」となった部分だけ記載します。 JSXのルールについて HTMLコードはラップする export default () => <div> <h1>挨拶</h1> <p>こんにちは</p> <footer>Copyright 2021</footer> </div> 複数行になるときはHTMLを括弧内に入れる export default () => ( <div> <h1>挨拶</h1> <p>こんにちは</p> <footer>Copyright 2021</footer> </div> ) classはclassNameと書く export default () => ( <div className="contents"> //classNameと記載 <h1>挨拶</h1> <p>こんにちは</p> <footer>Copyright 2021</footer> </div> ) style属性の値は{{}}に書く export default () => ( <div className="contents"> <h1 style={{ color: "red", fontWeight: "bold"}}>挨拶</h1> <p>こんにちは</p> <footer>Copyright 2021</footer> </div> ) 装飾目的のaltについて HTMLからJSXへの変換ツールなどを利用すると、altの表示が<img alt/>となっている場合があるが、ESLintErrorが発生するので<img alt="" />と書き換える altの変換も良い感じに仕上げてくれる変換ツールはこちら トップページ用に画像を追加したときのエラーについて static配下にimagesフォルダを作成して画像を追加し、gatsby developを実行すると下記のエラーがいっぱい表示された。。。 watchpack error (watcher): error: emfile: too many open files, watch こんなの書いてないよ・・・と項垂れつつ調べると自分のMacの設定されているファイルの上限の問題らしい。解決方法はこちら。 ファイルの上限を確認 $ launchctl limit maxfilesの上限が足りてないのが原因らしい。 設定ファイルを作成する $sudo vi /Library/LaunchDaemons/limit.maxfiles.plist 初期ではファイルはないはずなので新規作成となるはずなので 設定内容を追加を追加する。 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>limit.maxfiles</string> <key>ProgramArguments</key> <array> <string>launchctl</string> <string>limit</string> <string>maxfiles</string> <string>524288</string> <string>524288</string> </array> <key>RunAtLoad</key> <true/> <key>ServiceIPC</key> <false/> </dict> </plist> plistファイルを読み込み $ sudo launchctl load -w /Library/LaunchDaemons/limit.maxfiles.plist もう一回設定値を確認して増えていればOK $ launchctl limit 今回はここまで。 BuildしてNetlifyにデプロイまですると見た目はサイトらしくなりました。(途中経過キャプチャ) 詳しいgatsbyのことは次回以降から書いて行く予定です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者による初心者のためのGatsbyJS覚書

初心者による初心者のためのGatsbyJS覚書 その1 この記事について 会社でヘッドレスCMSに関するタスクをもらい、業務でGatsbyJSを少しだけさわった経験がある新卒2年目が作成しています。 参考資料として書籍を使用していますが、筆者がビギナークラスのため読んでいて「?」となる部分や間違っている箇所もあるかと思います。 参考までに、そして間違えている箇所がありましたらご連絡いただけると嬉しいです。 参考 今回はWebサイト高速化のための 静的サイトジェネレーター活用入門を使って学んでいきます。 MacBook Pro 2020 M1チップモデル node v12.18.3 Gatsby CLI version: 3.10.0 Gatsby version: 3.10.2 npm 6.14.6 下準備 GitHubアカウントの準備 Netlifyアカウントの登録 Contentfulアカウントの登録 Gitの諸設定 ひとまずGitの存在を確認 git -v GitHubに登録しているメールアドレスとユーザー名を登録 git config --global user.name "username" git config --global user.email "email" nvmのインストール(バージョンが更新されるので最新をチェック) nvmのGitHub curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash インストールを終えたらターミナルを一旦再起動して command -v nvm で「nvm」が表示されたらOK 注意 後にnpmコマンドでgatsbyをインストールしようとしたときエラーが発生し気が付いたが、MacOSの場合、先にパスを書き込むファイルの作成が必要だったらしい。。。(下記コマンドでエラー解消) エラー内容 npm ERR! errno -13 npm ERR! syscall access npm ERR! Error: EACCES: permission denied, access '/usr/local/lib/node_modules' npm ERR! { Error: EACCES: permission denied, access '/usr/local/lib/node_modules' ... ... ...続く 解決法(npmのデフォルトディレクトリを作成する) $ mkdir ~/.npm-global $ npm config set prefix '~/.npm-global' $ echo ' export PATH=~/.npm-global/bin:$PATH' >> ~/.bash_profile $ source ~/.bash_profile NodeJSのインストール まずはローカルでインストールされているか確認 nvm -ls インストールできるものを確認 nvm ls-remote 最新版をインストール nvm install --lts --latest-npm インストール後ローカルのバージョンを確認して nvm -ls バージョンを確認できたらOK node -v yarnのインストール curl -o- -L https://yarnpkg.com/install.sh | bash インストールを終えたらターミナルを一旦再起動して yarn -v Gatsbyのスターターを使って開発用のディレクトリを準備 Gastby CLIをインストール npm install -g gatsby-cli 動作確認 gatsby -v わかりやすいフォルダを作ってスターターをダウンロード。(今回はhello worldを使用) mkdir gatsby cd gatsby gatsby new mysite https://github.com/gatsbyjs/gatsby-starter-hello-world 開発サーバーを起動してみる cd mysite gatsby develop 無事起動できたらlocalhostにアクセスしてみる 静的ファイルを生成してみる gatsby build /publicディレクトリ配下にindex.htmlが生成され、develop時と同じように Hello worldと表示されることを確認してください。 GatsbyのアプリディレクトリをGitで管理できるようにする 注意 publicディレクトリがgitignoreに入っている場合、対象外としておく ※Netlifyでのデプロイの際Gitからpublicディレクトリを参照するため スターターでついてきたGitリポジトリを削除、新しいローカルリポジトリを作成してコミットしておく cd mysite rm -rf .git git init git add -A git commit -m "first commit" 自分のGitHubで新しいリポジトリを作ってリモートへセットしてプッシュ git remote add origin "新しいリポジトリURL" git push origin master Netlifyでデプロイしてみる コマンドでgatsby buildを一度でも実行していれば、 /public配下にindex.htmlが存在しているはず、なので、 この/publicを利用してデプロイの情報をセットしていきます。 もろもろの手順はこちらのPDF資料の3-3に記載があるので割愛。 キャプチャのBase directoryの部分、PDFには記載がなく、いろいろ試してみたが、空欄で良かったらしい。 デプロイが完成するとURLが表示されるようになるので、そちらにアクセスすると gatsby buildで生成した静的ファイルがサーバーで表示されているのが確認できる。 今回はここまで。 これでgatsby developで起動する仮想サーバーとgastby buildで生成するファイルの表示先のNetlify環境が完成したので今後は本に沿ってGatsbyJSでの開発を進めていきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

リアルタイムなLine風チャットアプリ(React + Firestore)

githubソース firebase-realtime-chat1 はじめに 画面イメージ Line風のチャットアプリです(複数人の投稿がリアルタイムに更新されます) 利用のためにはFirestoreアカウントが必要です(googleのアカウントがあれば利用できます) 初回利用時に名前を入力すると保存されます バックエンドのロジックはありません(DBとして利用するFirestoreの同期機能を利用) React + firebaseのSDKを利用します 利用モジュール create-react-app + firebase のSDKのみです。 "dependencies": { "@types/node": "^12.0.0", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "firebase": "^8.8.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "4.0.3", "typescript": "^4.1.2" } Firestoreについて Firebaseとは、Google が提供しているモバイル、Webアプリ向けのバックエンドサービスです。Firestoreというリアルタイムで同期ができるNoSQLデータベースを利用します。 Firebaseプロジェクトの作成方法は下記の外部ページでご確認ください。 [Firebase] プロジェクトを新規に作成する Firestore接続設定 .envファイルで設定します。 設定する内容はfirebaseのコンソールから確認します。 .env #Firebase configuration REACT_APP_API_KEY=XXXXXXXXXXXXXXXXXXX REACT_APP_AUTH_DOMAIN=myfirstproject-2b5c3.firebaseapp.com REACT_APP_PROJECT_ID=myfirstproject-2b5c3 REACT_APP_STORAGE_BUCKET=myfirstproject-2b5c3.appspot.com REACT_APP_MESSAGEING_SENDER_ID=XXXXXXXXXXXXXXXXXXX REACT_APP_APP_ID=XXXXXXXXXXXXXXXXXXX firebaseConfig.ts Firebase SDKの初期化を行いdbをエクスポートします。 import firebase from 'firebase'; const firebaseConfig = { apiKey: process.env.REACT_APP_API_KEY, authDomain: process.env.REACT_APP_AUTH_DOMAIN, projectId: process.env.REACT_APP_PROJECT_ID, storageBucket: process.env.REACT_APP_STORAGE_BUCKET, messagingSenderId: process.env.REACT_APP_MESSAGEING_SENDER_ID, appId: process.env.REACT_APP_APP_ID, }; const firebaseApp = firebase.initializeApp(firebaseConfig); const db = firebaseApp.firestore(); const auth = firebaseApp.auth(); export { auth }; export default db; ソースコード Line風の見た目はCSSだけでLINE風の「吹き出し」を作る方法を参考にさせていただきました。 html部分 チャットログはChatLog型の配列です。自分の名前のログを右側に、他人を左側に表示するようにclass名を切り替えています。 type ChatLog = { key: string, name: string, msg: string, date: Date, }; Chat1.tsx <> {/* チャットログ */} <div> {chatLogs.map((item, i) => ( <div className={userName===item.name? 'balloon_r': 'balloon_l'} key={item.key}> {userName===item.name? getStrTime(item.date): '' } <div className="faceicon"> <img src={userName===item.name? './img/cat.png': './img/dog.png'} alt="" /> </div> <div style={{marginLeft: '3px'}}> {item.name}<p className="says">{item.msg}</p> </div> {userName===item.name? '': getStrTime(item.date)} </div> ))} </div> {/* メッセージ入力 */} <form className='chatform' onSubmit={e => { submitMsg();e.preventDefault() }}> <div>{userName}</div> <input type="text" value={msg} onChange={(e) => setMsg(e.target.value)} /> <input type='image' onClick={submitMsg} src='./img/airplane.png' alt='' /> </form> </> データ同期処理 簡略化のため保存するテーブル名(collection)は固定です。 チャットルームのリファレンス(messagesRef)を取得して、最新10件を取得します。 onSnapshot()で変更イベントを受け取ります。別の人が追加したチャットメッセージを受け取り、ログに追加します。 削除、更新イベントも発生しますが、動作に影響ないため追加のみを処理します。 const [chatLogs, setChatLogs] = useState<ChatLog[]>([]); const messagesRef = useMemo(() => db.collection("chatroom").doc("room1").collection("messages"), []); useEffect( () => { // 同期処理イベント(最新10件をとるためdateでソート) messagesRef.orderBy("date", "desc").limit(10).onSnapshot((snapshot) => { snapshot.docChanges().forEach((change) => { if (change.type === "added") { // チャットログへ追加 addLog(change.doc.id, change.doc.data()); // 画面下部へスクロール window.scroll(0, document.documentElement.scrollHeight - document.documentElement.clientHeight) } }); }); },[]); チャットメッセージ追加処理 /** * チャットログに追加 */ function addLog(id: string, data: any) { const log = { key: id, ...data, }; // Firestoreから取得したデータは時間降順のため、表示前に昇順に並び替える setChatLogs((prev) => [...prev, log,].sort((a,b) => a.date.valueOf() - b.date.valueOf())); } ソースコード全体 import React, {useState, useMemo, useEffect} from 'react'; import db from './firebaseConfig'; import './chat1.css'; type ChatLog = { key: string, name: string, msg: string, date: Date, }; /** * ユーザー名 (localStrageに保存) **/ const getUName = (): string => { const userName = localStorage.getItem('firebase-Chat1-username'); if (!userName) { const inputName = window.prompt('ユーザー名を入力してください', ''); if (inputName){ localStorage.setItem('firebase-Chat1-username', inputName); return inputName; } } return userName; } /** * UNIX TIME => hh:mm **/ function getStrTime(time: any){ let t = new Date(time); return `${t.getHours()}`.padStart(2, '0') + ':' + `${t.getMinutes()}`.padStart(2, '0'); } /** * チャットコンポーネント(Line風) */ const Chat1: React.FC = () => { const [chatLogs, setChatLogs] = useState<ChatLog[]>([]); const [msg, setMsg] = useState(''); const userName = useMemo(() => getUName(), []); const messagesRef = useMemo(() => db.collection("chatroom").doc("room1").collection("messages"), []); useEffect( () => { // 同期処理イベント(最新10件をとるためdateでソート) messagesRef.orderBy("date", "desc").limit(10).onSnapshot((snapshot) => { snapshot.docChanges().forEach((change) => { if (change.type === "added") { // チャットログへ追加 addLog(change.doc.id, change.doc.data()); // 画面下部へスクロール window.scroll(0, document.documentElement.scrollHeight - document.documentElement.clientHeight) } }); }); // eslint-disable-next-line react-hooks/exhaustive-deps },[]); /** * チャットログに追加 */ function addLog(id: string, data: any) { const log = { key: id, ...data, }; // Firestoreから取得したデータは時間降順のため、表示前に昇順に並び替える setChatLogs((prev) => [...prev, log,].sort((a,b) => a.date.valueOf() - b.date.valueOf())); } /** * メッセージ送信 */ const submitMsg = async () => { if (msg.length === 0) { return; } messagesRef.add({ name: userName, msg: msg, date: new Date().getTime(), }); setMsg(""); }; return ( <> {/* チャットログ */} <div> {chatLogs.map((item, i) => ( <div className={userName===item.name? 'balloon_r': 'balloon_l'} key={item.key}> {userName===item.name? getStrTime(item.date): '' } <div className="faceicon"> <img src={userName===item.name? './img/cat.png': './img/dog.png'} alt="" /> </div> <div style={{marginLeft: '3px'}}> {item.name}<p className="says">{item.msg}</p> </div> {userName===item.name? '': getStrTime(item.date)} </div> ))} </div> {/* メッセージ入力 */} <form className='chatform' onSubmit={e => { submitMsg();e.preventDefault() }}> <div>{userName}</div> <input type="text" value={msg} onChange={(e) => setMsg(e.target.value)} /> <input type='image' onClick={submitMsg} src='./img/airplane.png' alt='' /> </form> </> ); }; export default Chat1; 参考ページ [Firebase] プロジェクトを新規に作成する [Firebase] Firestoreで読み書きする (Web編) [Firebase] Firestoreでリアルタイムなチャットを作る (Web編) その1 CSSだけでLINE風の「吹き出し」を作る方法
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactで画像を表示する方法

Reactで画像を表示する方法 今回、Reactで画像を表示させる際、3つの方法を試してみました。 ①JSXのみに書き込む方法 ②画像ファイル、JSXの両方に画像のパスを書き込む方法 ③画像ファイルのパスをインポートし、コンポーネントに表示させる方法 結果 ①失敗 ②失敗 ③成功!! 結果は③の③画像ファイルのパスをインポートし、コンポーネントに表示させる方法でのみ成功しました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[JS]mutableとimmutableでの配列操作について

はじめに 自身のおさらいも含め、初学者の方に向けて、JavaScriptでの配列操作についてまとめていきたいと思います。 [対象読者] JavaScript / React初学者 [記事のテーマ] mutable / immutableとは? フロントエンド開発における、非破壊的な配列操作について Reactでアプリケーションを作っていく際は、副作用のない純粋な関数を使用することが推奨されます。純粋な関数であるためには、以下の条件が必要です。 ・最低1つの引数を持つ ・1つの値、もしくは1つの関数を返す ・引数を変化させてはいけない (immutable) ← 今回の記事のテーマ mutable, immutableとは? JSの配列操作等のキーワードで検索をかけると、mutable(破壊的なメソッド)とimmutable(非破壊的なメソッド)というキーワードを良く目にするかと思いますが、破壊的なメソッドとは、対象となる元の配列の値を変えてしまうメソッドのことを指します。 以下に破壊的なメソッドと非破壊的なメソッドの違いを示します。 破壊的なメソッドは元の配列を直接操作(=破壊)する 破壊的なメソッド const nums = [1,2,3,4,5]; // 〜何らかの処理〜 nums.push(6); // 〜何らかの処理〜 console.log(nums); // 読み手は[1,2,3,4,5]を期待する 非破壊的なメソッドは、元の配列のコピーに対して配列操作する 非破壊的なメソッド const nums = [1,2,3,4,5]; // 〜何らかの処理〜 const newNums = nums.concat(6); // 〜何らかの処理〜 console.log(nums); // [1,2,3,4,5] 読み手の期待通り console.log(newNums) // [1,2,3,4,5,6] こちらも読み手の期待通り 上記のように、破壊的なメソッドを使用して配列操作すると、不要な混乱を招くことになります。JavaScriptには、いくつか破壊的なメソッドが用意されており、以下メソッドを使う時は、注意が必要です。 破壊的なメソッド splice() push() pop() shift() unshift() sort() reverse() fill() copyWithin() 破壊的な配列操作をする際も、以下のように、元の配列を変えてしまわないようにスプレッド演算子でコピーを作って操作できないか検討してみましょう。以下はsort()を使用する際に、スプレッド演算子を使用して、破壊的な操作を回避した例です。 スプレッド演算子を使用して非破壊的な操作を const articleIds = [3,5,1,4,2]; const sortedArticleIds = [...articleIds].sort(); console.log(sortedArticleIds); // [1,2,3,4,5] ユースケース別の実装例 1.配列を加工したい ES6には複数の便利メソッドが用意されてますが、ここではよく利用されるであろうmap()を紹介します。 まずは破壊的な操作 破壊的な配列加工 let nums = [1, 2, 3]; for (var i = 0; i < nums.length; i++) { nums[i] = nums[i] * 2; } console.log(nums); // [ 2, 4, 6 ] 上記を非破壊的な操作に書き換えると以下になります。 mapを使用して非破壊的な配列加工をする const nums = [1, 2, 3]; const doubled = nums.map((num) => { return num * 2; }); console.log(nums); // [ 1, 2, 3 ] console.log(doubled); // [ 2, 4, 6 ] 2.要素を追加したい push() unshift() まずは、破壊的なメソッド。 要素を追加したい:破壊的[push、unshift] // push() const nums_push = [1,2,3,4,5]; nums_push.push(6); console.log(nums_push); // [1,2,3,4,5,6] // unshift() const nums_unshift = [1,2,3,4,5]; nums_unshift.unshift(0); console.log(nums_unshift); // [0,1,2,3,4,5] 非破壊的な操作に書き直します。 要素を追加したい:非破壊的 // popを非破壊的に書き直す const nums = [1,2,3,4,5]; const newNums = [...nums, 6]; console.log(nums); // 1,2,3,4,5 console.log(newNume); // 1,2,3,4,5,6 // unshiftを非破壊的に書き直す const nums = [1,2,3,4,5]; const newNums = [0, ...nums]; console.log(nums); // 1,2,3,4,5 console.log(newNume); // 0,1,2,3,4,5 3.要素を削除したい pop() shift() まずは、破壊的なメソッド。 要素を追加したい:破壊的[pop、shift] // pop() const nums_pop = [1,2,3,4,5]; nums_pop(); console.log(nums_pop); // [1,2,3,4] // shift() const nums_shift = [1,2,3,4,5]; nums_unshift.shift(); console.log(nums_unshift); // [2,3,4,5] 非破壊的な操作に書き直します。 要素を追加したい:非破壊的 const nums = [1,2,3,4,5]; const newNums = nums.filter(num => num !== 3) console.log(newNums) // 1,2,4,5 最後に 以上、駆け足ではありましたが、mutable / immutableな配列操作の違いの解説でした。相互レビューをする際に、不要な破壊的操作を行っているコードは指摘されるポイントになるので、非破壊的な操作は意識しながら開発するのが良いと思います。何故immutableである方が望ましいのかは別記事でもう少し掘り下げようかと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gatsby で雑にマークダウン形式ブログを作る

「そうだ、自作ブログを作ろう。」 「どうせなら Qiita の記事をそのままブログに載せられるようにしたい。」 「もっというと、マークダウン形式のファイルをそのままブログに突っ込めるようにしたい。」 「でも面倒なことはしたくない。サーバーすら立てたくない」 こんなわがままでもブログが作れてしまいます。 そう、Gatsby ならね。 前準備 npm がインストールされている前提で進めます。 まずは Gatsby のプロジェクトを作成しましょう 公式のチュートリアルを見つつ、コマンドをぽちぽち叩いていきましょう。 npm install -g gatsby-cli gatsby new {任意のプロジェクト名} これで完了です。早い。 生成されたディレクトリに移動し、npm run developを実行することで Gatsby のデフォルトページが閲覧できます。 TypeScript を使いたい場合はsrc/pagesディレクトリ中のjsファイルの拡張子をtsxに変えましょう。もともと TypeScript での開発も想定されているようで、これだけで機能します。すごい。 マークダウンファイルのパース 下準備はできたので、早速本題に入りましょう。 マークダウンをパースするためにはgatsby-transformer-remarkを使います。 npm install gatsby-transformer-remarkでインストールし、gatsby-config.jsに設定を加えましょう。 gatsby-config.js plugins: [ `gatsby-transformer-remark`, ], プラグイン項目に記述を追加するだけで動作してくれます。 次に、実際に読み込みたいマークダウンファイルを追加します。 プロジェクトのルートディレクトリ配下ならどこでもいいのですが、今回はsrc/markdown配下に追加することとします。 適当に記述したマークダウンファイルを配置できたら、再びgatsby-config.jsに記述を追加しましょう。 gatsby-config.js plugins: [ { resolve: `gatsby-source-filesystem`, options: { name: `markdown`, path: `${__dirname}/src/markdown`, }, }, ] こちらの設定はマークダウンファイルを Gatsby のエコシステムで認識できるようにするためのものです。 ここまで設定できたら、一度ローカル環境で起動し、http://localhost:8000/___graphqlにアクセスしてみましょう。 Gatsby では GraphQL を採用しており、様々な情報を GraphQL 経由で取得することができます。 今回追加したマークダウンファイルについても同様で、上記の設定を追加することで GraphQL から取得できようになっています。 下記のクエリを投げてみましょう。 query MyQuery { allMarkdownRemark { nodes { html } } } 問題がなければ、先ほど追加したマークダウンファイルが HTML 形式で取得できているはずです。 Gatsby への追加 では、上記で取得できた HTML を Gatsby に追加していきましょう。 先ほど行った修正により、マークダウンファイルを HTML 形式で GraphQL 経由で取得することができるようになっています。 Gatsby ではこの GraphQL を利用することができます。 index.js import * as React from "react" import { graphql } from "gatsby" import Layout from "../components/layout" export const query = graphql` query MyQuery { allMarkdownRemark { nodes { id html } } } ` const IndexPage = ({data}) => ( <Layout> <div dangerouslySetInnerHTML={{__html: data.allMarkdownRemark.nodes[0].html}}></div> </Layout> ) export default IndexPage GraphQL で取得したデータは、引数にdataを指定することで読み込むことができます。簡単ですね。 HTML 形式で取得しているので、あとはそのまま出力するように突っ込むだけです。 今回利用しているdangerouslySetInnerHTMLは React で提供されている属性です。 わざわざdangerouslyとしているのは XSS の危険性があるためです。詳しいことは公式のドキュメントを参照してください。 あとはnpm run developなどで結果を確認するだけです。 うまく設定できていれば、マークダウンファイルの中身がちゃんと表示されているはずです。 おわりに というわけで、Gatsby なら簡単にマークダウン形式のブログが作れるよ、という記事でした。 私は上記に加え Github Pages を利用することで、簡単な俺俺ブログにしています。 厳密には Qiita では改行コードがそのまま改行として扱われますが、今回作成したものではちゃんとスペースを2つ並べなければ改行されません。 とはいえあとはほぼそのままで動きますし、改行させるだけなら大した手間ではないでしょう。 Gatsby は便利だなぁ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactでコンポーネントのプロパティーにイベントを渡すときの型を整理

Reactで定義されているイベントの型がわからずエラーメッセージを見ながら調整していくことになるので型を整理 サンプルコードはmaterial-uiで定義されているコンポーネントを呼び出すと仮定しています。 onChange テキストフィールドなどのフォームで値の入力値を検知してアクションを実行する時に出てくる (event: React.ChangeEvent<{}>) => void; interface Props { changeTextFieldHandler: (event: React.ChangeEvent<{}>) => void; } <TextField onChange={changeTextFieldHandler} /> onSubmit formのサブミット (event: React.FormEvent) => void; interface Props { formSubmitAction: (event: React.FormEvent<HTMLFormElement>) => void; } <form onSubmit={formSubmitAction}> onMouseEnter / onMouseLeave コンポーネントにマウスオーバーした時、マウスオーバーを解除したときのアクション React.MouseEventHandler; interface Props { onMouseEnterAction: React.MouseEventHandler<HTMLButtonElement>; onMouseLeaveAction: React.MouseEventHandler<HTMLButtonElement>; } <Button onMouseEnter={onMouseEnterAction} onMouseLeave={onMouseLeaveAction} /> onClick クリックイベント (event: React.MouseEvent) => void; interface Props { onClickHandler: (event: React.MouseEvent<HTMLButtonElement>) => void; } <Button onClick={onClickHandler}/>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む