- 投稿日:2021-02-27T22:04:31+09:00
Gatsby.jsでポートフォリオを作ろうと思い立ってからNetlifyで公開するまでをまとめてみた。
背景
今流行っているGatyby.jsで自身のポートフォリオを作ってみたいなぁと思い立ち、
実際に作ってみたので、その流れをまとめてみました。この記事で得られるもの
Gatsby.jsを最小構成でセットアップする方法
GithubからNetlifyへのデプロイ方法
ポートフォリオの画面イメージを練るプロセス
最終的な成果物
最終的に下記のポートフォリオを作成し、Netlifyで公開しました。
https://annos-portfolio.netlify.app/
ソースコードは下記で公開してます。
https://github.com/Anno328/Portfolio環境/前提
上記ライブラリがインストールされていること。
GitHub上にリポジトリがあること。
Netlifyのアカウントがあること。
手順
ポートフォリオの画面イメージを練る
デザインについては全くの素人なので、他人様が公開してくださっているポートフォリオをもとに画面イメージを練っていきます。
下記サイトが参考になりました。
https://codeburst.io/10-awesome-web-developer-portfolios-d266b32e6154で、練った画面イメージは下記のようになりました。
(青字でHeaderとか言ってるのは、コンポーネントです。)コンポーネント設計
次にコンポーネント設計をしていきます。
画面イメージができたので、画面をセクションに区切って、コンポーネントとして切り分けていきます。
そして、切り分けたコンポーネントの親子関係を図示していきます。Gatsbyセットアップ
ここからGatybyのセットアップに入っていきます。
そもそもGatsby.jsってなんぞや?って方は下記をご覧ください。
https://www.gatsbyjs.com/Gatsby.jsはReactで作られた静的サイトジェネレーターです。内部的にGraphQLを用いてデータを取得し、markdownからHTMLを生成、などの処理を簡単に行うことができます。
https://qiita.com/hppRC/items/00739eaf9ae7fc95c1ca下記の公式チュートリアルに沿ってセットアップしていきます。
(公式チュートリアルは親切にもnpmやらgitやらのインストールから解説してくれてます。。インストール済みの方はすっ飛ばしてOKです。)
https://www.gatsbyjs.com/docs/tutorial/part-zero/#create-a-gatsby-site1)npm initでアプリケーションの初期処理を行う。
任意のフォルダで下記を実施し、アプリケーションに必要な設定ファイル(package.json)を作成していきます。
npm initコマンド実行後に指定が必要な各種項目については、下記サイトが参考になります。
https://techacademy.jp/magazine/16151こだわりがなければエンターを押し続けてもらえればOKです。
2)gatsby-cliをインストールする。
1)で作成したフォルダに移動し、下記コマンドを実行することでgatsby-cliをインストールすることができます。
npm install -g gatsby-cligatsby-cliとは、Gatsby.jsアプリを作成したり、操作するために必要なライブラリです。
これをインストールすることで、gatsbyコマンドが使えるようになります。3)Gatsbyサイトを作成する。
下記コマンドを実行し、Gatsbyサイトを作成します。
今回はチュートリアルに沿って、hello-worldスタータキットを用いて作成します。gatsby new hello-world https://github.com/gatsbyjs/gatsby-starter-hello-world4)サーバを起動して、ページが表示されることを確認する。
3)で作成しhello-worldフォルダに移動し、サーバを起動するコマンドをたたきます。
cd hello-world gatsby developこれでサーバが立ち上がりました。
実際に確認するために、ブラウザから下記URLにアクセスします。
http://localhost:8000/上記画面が表示されればOKです。
セットアップは完了です!実装
ここからは実装です。
このトピックについては本記事で詳しく説明することはしないのですが、基本的にはコンポーネント設計をもとに作成していきます。
コードは下記で公開しているので、よかったら参照してみてください。
https://github.com/Anno328/PortfolioGitHubのリポジトリへコードをpush
ここまでで出来上がったソースコードをGitHubの任意のリポジトリにpushします。
リポジトリがない方は、下記を参考に作成しましょう。
https://qiita.com/sodaihirai/items/caf8d39d314fa53db4dbNetlifyへデプロイ
準備ができたので、GitHubとNetlifyを連携させて、デプロイしていきます。
そもそもNetlifyってなんぞや?って方は下記をご覧ください。
https://www.netlify.com/静的コンテンツのホスティングサービス。
使い勝手の良いUIと、簡単な手順で公開までできるのがめっちゃよい。
GitHub / GitLab / Bitbucket のリポジトリと連携して、pushやmergeなどがあったらNetlifyCIがビルドやデプロイをしてくれる感じになっている。
https://qiita.com/sugo/items/2ee64887d682b0dae635Netlifyにログインする。
下記にアクセスし、ログインします。
https://www.netlify.com/GitHubと連携
次に、「New site from Git」を押下し、GitHubの任意のリポジトリと連携します。
Create a new site で「GitHub」を選択します。
ビルド対象のブランチ・ビルドコマンド・ビルド結果のパスを入力し、「Deploy site」を押下します。
これでGitHubとの連携は完了です!ビルドが完了したら、下記URLよりデプロイしたサイトを確認することができます。
まとめ
Gatsby.jsでポートフォリオを作ろうと思い立ってからNetlifyで公開するまでの手順
1. 他人様のポートフォリオを参考にし、画面イメージを練る
2. 画面イメージをもとに、コンポーネント設計をする。
3. 設計をもとに、実装する。
4. 出来上がったソースコードをGitHubにpushする。
5. GitHubとNetlifyを連携し、Netlifyにデプロイする。所感
Gatsby.jsで作ってみたはいいものの、あまりGatsbyであることのメリットを活かせていない気がしています。。
(imageとかもGatsbyのプラグインじゃなくてimgタグで埋めこんでしまっている。)
そもそも静的にサイトをGenerateしてないしね!もはやGatsbyというか、ただのReactベースになってしまったので、もっとプラグインとか活用してしけたらいいなと思います。
やっぱりブログとか記事を増やしていきたいサービスに向いてるのかなと思いました。
ただサーバ勝手に作ってくれたり、静的コンテンツいい感じにまとめてくれたり、その辺の恩恵にあやかれるのは強いですね。あとNetlifyへのデプロイが楽すぎて感動しました...!
地味に特定ブランチにpushした時の自動デプロイトリガーもいつの間にか設定してあったので、今度ちゃんと調べてみようと思います。
- 投稿日:2021-02-27T19:56:10+09:00
Reactの公式チュートリアルを進化させる(2)
はじめに
Reactの公式チュートリアルを進化させる(1)の続きです.
React公式から与えられた課題はこれでした.まだ時間がある場合や、今回身につけた新しいスキルを練習してみたい場合に、あなたが挑戦できる改良のアイデアを以下にリストアップしています。後ろの方ほど難易度が上がります:
1. 履歴内のそれぞれの着手の位置を (col, row) というフォーマットで表示する。
2. 着手履歴のリスト中で現在選択されているアイテムをボールドにする。
3. Board でマス目を並べる部分を、ハードコーディングではなく 2 つのループを使用するように書き換える。
4. 着手履歴のリストを昇順・降順いずれでも並べかえられるよう、トグルボタンを追加する。
5. どちらかが勝利した際に、勝利につながった 3 つのマス目をハイライトする。
6. どちらも勝利しなかった場合、結果が引き分けになったというメッセージを表示する。今回は課題2に取り組みます.
課題内容
課題2の内容はこちら
- 着手履歴のリスト中で現在選択されているアイテムをボールドにする。
実装済みのやり直しボタンのうち,表示されている盤面の状態を表しているものを太字にしろということでしょうか.
ということでしょう.
実装
CSSで太字用クラスを用意して,太字にしたいときにそのクラスを割り当てるという方針でいきます.
まずは,index.cssに太字用クラスを追記します.
index.css/* 太字クラスを追加 */ .bold { font-weight: bold; }あとは,「現在選択されているアイテム」にだけこれを割り当てれば良いだけです.
stepNumberは現在のターンを表しているので,ボタンのうち上からstepNumber番目を太字にすれば良さそうです.index.jsを開いて,Gameコンポーネントのrenderメソッドのmovesの部分を以下のように書き換えましょう.
index.jsconst moves = history.map((value, index) => { const desc = index ? `Go to move #${index} (${(value.position % 3) + 1},${Math.floor(value.position / 3) + 1})`: 'Go to game start'; return ( <li key={index}> <button onClick={() => this.jumpTo(index)} className={index == this.state.stepNumber ? 'bold' : ''}> // [追加行] indexとstepNumberが等しいときboldを割り当てる {desc} </button> </li> ); });実装例
課題2終了時点のコードです.
index.jsimport React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; // Square関数コンポーネント function Square(props) { return ( <button className="square" onClick={props.onClick}> {props.value} </button> ); } // Boardクラスコンポーネント class Board extends React.Component { renderSquare(i) { return <Square value={this.props.squares[i]} onClick={() => this.props.onClick(i)} />; } render() { return ( <div> <div className="board-row"> {this.renderSquare(0)} {this.renderSquare(1)} {this.renderSquare(2)} </div> <div className="board-row"> {this.renderSquare(3)} {this.renderSquare(4)} {this.renderSquare(5)} </div> <div className="board-row"> {this.renderSquare(6)} {this.renderSquare(7)} {this.renderSquare(8)} </div> </div> ); } } // Gameクラスコンポーネント class Game extends React.Component { constructor(props) { super(props); this.state = { history: [{ squares: Array(9).fill(null), position: null, }], stepNumber: 0, xIsNext: true, }; } handleClick(i) { const history = this.state.history.slice(0, this.state.stepNumber + 1); const current = history[history.length - 1]; const squares = current.squares.slice(); if (calculateWinner(squares) || squares[i]) { return; } squares[i] = this.state.xIsNext ? 'X' : 'O'; this.setState({ history: history.concat([{ squares: squares, position: i, }]), stepNumber: history.length, xIsNext: !this.state.xIsNext, }); } jumpTo(step) { this.setState({ stepNumber: step, xIsNext: (step % 2) === 0, }); } render() { const history = this.state.history; const current = history[this.state.stepNumber]; const winner = calculateWinner(current.squares); const moves = history.map((value, index) => { const desc = index ? `Go to move #${index} (${(value.position % 3) + 1},${Math.floor(value.position / 3) + 1})`: 'Go to game start'; return ( <li key={index}> <button onClick={() => this.jumpTo(index)} className={index == this.state.stepNumber ? 'bold' : ''}>{desc}</button> </li> ); }); let status; if (winner) { status = 'Winner: ' + winner; } else { status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O'); } return ( <div className="game"> <div className="game-board"> <Board squares={current.squares} onClick={(i) => this.handleClick(i)}/> </div> <div className="game-info"> <div>{status}</div> <ol>{moves}</ol> </div> </div> ); } } // calculateWinner関数コンポーネント function calculateWinner(squares) { const lines = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6], ]; for (let i = 0; i < lines.length; i++) { const [a, b, c] = lines[i]; if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { return squares[a]; } } return null; } // ======================================== ReactDOM.render( <Game />, document.getElementById('root') );
- 投稿日:2021-02-27T18:32:26+09:00
毎日投稿(React)
本日はReactの学習しました。
はじめて触れる言語のため、ワクワクでした!
とりあえず、コードを打ちまくりでした。U-demyの動画を参考に学習しました!
モダンJavaSciptの基礎から始める挫折しないためのReact入門
https://www.udemy.com/course/modern_javascipt_react_beginner/JavaScriptの基本的な部分を抑えつつ、
Reactを始めていったので、確かにJavaScriptは大事な存在だとこのコースを受けて思いました。
(まだReactでTODOアプリを作っていないのですが。笑)※明日やります。今回は関数コンポーネントを使って”お元気ですか?”を呼び出し、またstyleもかけています。
以下、例です。index.js import React, { useEffect, useState } from "react"; import { ColorfulMessage } from "./Componets/ColorfulMessage"; export const App = () => { return ( <> <h1 style={{ color: "red" }}>kodamaだよ。</h1> <ColorfulMessage color="blue">お元気ですか?</ColorfulMessage> </> };propsには格納しているのが以下の
{color: "blue", children: "お元気ですか?"}⬇️ColorfulMessage.jsx import React from "react"; export const ColorfulMessage = (props) => { const { color, children } = props; //ここで分割代入か〜 const styleContent = { color, fontSize: "20px" }; return <p style={styleContent}>{children}</p>; };useEffect
自分的にはここがちょっとわからなかったポイントです。
useEffect....
動画を何度も見返したのですが、勘の鈍い私にはわからなかったので、公式のドキュメントを読んだりしたのですが、ちょっと理解できるものではなかったです。今の認識であっているかわからないですが、
useEffectととして機能を使うならば、第二引数に変化したい変数を配列に格納する!
今回はnumが変化していくので入れています。useEffect(() => { if (num > 0) { if (num % 3 === 0) { Faceflag || setFaceflag(true); } else { Faceflag && setFaceflag(false); } } }, [num]);まだまだレンダリングがどこで起きるかわからないところが多いので、
console.logを使いながら、やっていきたいと思います。
- 投稿日:2021-02-27T18:19:55+09:00
学習週次報告 #3
お疲れ様です。
この記事は、すごく個人的な学習週次報告です。
自分自身の「目標の明確化」「学んだこと・わからないことの整理」「成長記録」のために書いています。今回は、”今週で主にやったこと”から、
今週完成させた”自己紹介サイトについて”と、
”今週で学んだこと”、”つまづいたところ”、
そして、”次週何をやるべきか”を書いていきます。
今週で主にやったこと
- React、ReacrRauter、Redux、ReactRedux、ReduxThunkの復習
- React:簡易的な掲示板の作成
- ReacrRauter:APIを使ったクイズアプリの作成
- Redux:TODOを管理できるツールの作成
- ReactRedux:Reduxで作ったツールを元にTODOアプリの作成
- ReduxThunk:ReacrRauterで作ったクイズアプリのデータをStore管理できるようにする
- ”自己紹介サイト”の制作
- テストの復習(mocha、power-assert)
自己紹介サイトについて
- 一週間かかったか掛からなかったかで一応大体は実装し、公開した
- まだ修正すべきところは残るものの、基本的な動作は問題なさそうなので、
身近の人のフィートバックをもらいながら、今後こまごま直して行こうと思う- HashRouterでルーティングし、GitHub Pagesでデプロイ
- 静的ファイルを配信できるホスティングサービスに配置(GitHub Pages)することで、
バックエンドプログラムを動かすのに必要なサーバーを用意しなくても良かったから- 構成はSPA(のつもり)で作り、意図的にページ遷移を最小限にした。あまりに複雑だと見にくいと感じたから
- レスポンシブはCSSのメディアクエリで済ませた
使用したサイトや技術
- フリー素材サイト
- picjumbo(https://picjumbo.com/) と
pexels(https://www.pexels.com/ja-jp/)
がおしゃれでよかった- Material ui
- ボタンとアイコンとドローメニューのコンポーネントに使用
- React向けのUIコンポーネントライブラリの中でも人気が高かったから
- Material UI : 56.5k
- React-Bootstrap : 17.4k
- Reactstrap : 9k
- Material ui の基本はこの記事にお世話になりました(https://qiita.com/h-yoshikawa44/items/efa33101b0a02cba7759)
- あとは公式ドキュメントを和訳したりしながら実装した
今週で学んだこと
- HashRouterでGitHub Pagesに公開した場合(他に原因があるのかも)、 サイト内遷移はaタグではエラーになり、その代わりにreact-router-domのLinkを使わなければならない
つまづいたところ
- 自己紹介サイトをローカルからGitHubにプッシュする時にエラー
- error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: CANCEL (err 8)
次週何をやるべきか
- 忘れないようにまたReact、ReacrRauter、Redux、ReactRedux、ReduxThunkの復習
- 次はデータベースを使う動的なアプリを開発したいので、基本的なバックエンドの勉強
内容は以上です。
みなさんお互いに頑張りましょう。千里の道も一歩から by 老子
- 投稿日:2021-02-27T17:43:50+09:00
Reactの公式チュートリアルを進化させる(1)
はじめに
Reactの公式チュートリアルでは,三目並べゲームの実装方法が紹介されていますが,このページの最後にこんな記述があります.
まだ時間がある場合や、今回身につけた新しいスキルを練習してみたい場合に、あなたが挑戦できる改良のアイデアを以下にリストアップしています。後ろの方ほど難易度が上がります:
1. 履歴内のそれぞれの着手の位置を (col, row) というフォーマットで表示する。
2. 着手履歴のリスト中で現在選択されているアイテムをボールドにする。
3. Board でマス目を並べる部分を、ハードコーディングではなく 2 つのループを使用するように書き換える。
4. 着手履歴のリストを昇順・降順いずれでも並べかえられるよう、トグルボタンを追加する。
5. どちらかが勝利した際に、勝利につながった 3 つのマス目をハイライトする。
6. どちらも勝利しなかった場合、結果が引き分けになったというメッセージを表示する。やるしかないですね.
てことで,実装したので課題ごとに載せていこうと思います.
本記事では課題1のみ記載します.
当方React初心者ですので,何かあればアドバイスいただけると幸いです.なお,この記事はReactの公式チュートリアルを終えていることを前提に書いています.
公式チュートリアルで解説されていることについては詳しく触れないのでご承知おきください.課題内容
最初の課題内容はこちらです.
- 履歴内のそれぞれの着手の位置を (col, row) というフォーマットで表示する。
公式チュートリアルでは,着手の履歴を保存しておくことにより,過去のターンに戻ってやり直せるタイムトラベル機能を実装しています.
チュートリアルでは,下画像のように「Go to move #(ターン)」という形式で表示されたボタンをクリックすることで戻ることができるのですが,
下画像のように,そのターンで選択したマスを(列,行)という形式で表示することで分かりやすくしなさい,という主旨の課題のようです.
実装
チュートリアルでは,着手の履歴として盤面の状態のみを保存していましたが,選択したマスの位置も保存することにします.
Gameコンポーネントのstatethis.state = { history: [{ // 着手の履歴を保存 squares: Array(9).fill(null), // 盤面の状態 position: null, // [追加行] 選択したマスの位置(squaresのindex) }], stepNumber: 0, // ターン数 xIsNext: true, // 手番 };これに伴い,stateの更新を行うhandleClickメソッドも以下のように変更します.
handleClickメソッドは,盤面のマスがクリック(選択)された際の処理です.GameコンポーネントのhandleCkickhandleClick(i) { const history = this.state.history.slice(0, this.state.stepNumber + 1); // 着手の履歴 const current = history[history.length - 1]; // 現在の状態 const squares = current.squares.slice(); // 現在の盤面 if (calculateWinner(squares) || squares[i]) { // 決着がついている or 既に選択されたマス だったら return; } squares[i] = this.state.xIsNext ? 'X' : 'O'; // 手番でXかOか決める this.setState({ // stateの更新 history: history.concat([{ squares: squares, position: i, // [追加行] }]), stepNumber: history.length, xIsNext: !this.state.xIsNext, }); }JSXの生成を行っているmovesの内容も変更します.
Gameコンポーネントrenderメソッドのmovesconst moves = history.map((value, index) => { const desc = index ? `Go to move #${index} (${(value.position % 3) + 1},${Math.floor(value.position / 3) + 1})`: // [変更行] (列,行) 'Go to game start'; return ( <li key={index}> <button onClick={() => this.jumpTo(index)}>{desc}</button> </li> ); });positionは,9個のマスに対して0~8の通し番号を左上から振った場合の位置を表しているため,
列番号はvalue.position % 3) + 1
行番号はMath.floor(value.position / 3) + 1
とすることで表せます.チュートリアルでは,mapのコールバック関数の引数として,(step, move)を使用していましたが,個人的に分かりづらかったので(value, index)に変更しています.(step, move)のままでも構いません.
実装例
課題1終了時点のコードを載せておきます.
index.jsimport React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; // Square関数コンポーネント function Square(props) { return ( <button className="square" onClick={props.onClick}> {props.value} </button> ); } // Boardクラスコンポーネント class Board extends React.Component { renderSquare(i) { return <Square value={this.props.squares[i]} onClick={() => this.props.onClick(i)} />; } render() { return ( <div> <div className="board-row"> {this.renderSquare(0)} {this.renderSquare(1)} {this.renderSquare(2)} </div> <div className="board-row"> {this.renderSquare(3)} {this.renderSquare(4)} {this.renderSquare(5)} </div> <div className="board-row"> {this.renderSquare(6)} {this.renderSquare(7)} {this.renderSquare(8)} </div> </div> ); } } // Gameクラスコンポーネント class Game extends React.Component { constructor(props) { super(props); this.state = { history: [{ squares: Array(9).fill(null), position: null, }], stepNumber: 0, xIsNext: true, }; } handleClick(i) { const history = this.state.history.slice(0, this.state.stepNumber + 1); const current = history[history.length - 1]; const squares = current.squares.slice(); if (calculateWinner(squares) || squares[i]) { return; } squares[i] = this.state.xIsNext ? 'X' : 'O'; this.setState({ history: history.concat([{ squares: squares, position: i, }]), stepNumber: history.length, xIsNext: !this.state.xIsNext, }); } jumpTo(step) { this.setState({ stepNumber: step, xIsNext: (step % 2) === 0, }); } render() { const history = this.state.history; const current = history[this.state.stepNumber]; const winner = calculateWinner(current.squares); const moves = history.map((value, index) => { const desc = index ? `Go to move #${index} (${(value.position % 3) + 1},${Math.floor(value.position / 3) + 1})`: 'Go to game start'; return ( <li key={index}> <button onClick={() => this.jumpTo(index)}>{desc}</button> </li> ); }); let status; if (winner) { status = 'Winner: ' + winner; } else { status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O'); } return ( <div className="game"> <div className="game-board"> <Board squares={current.squares} onClick={(i) => this.handleClick(i)}/> </div> <div className="game-info"> <div>{status}</div> <ol>{moves}</ol> </div> </div> ); } } // calculateWinner関数コンポーネント function calculateWinner(squares) { const lines = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6], ]; for (let i = 0; i < lines.length; i++) { const [a, b, c] = lines[i]; if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { return squares[a]; } } return null; } // ======================================== ReactDOM.render( <Game />, document.getElementById('root') );
- 投稿日:2021-02-27T14:56:18+09:00
Property 'hogehoge' does not exist on type 'DefaultRootState'を解決する【React/Redux】
はじめに
React+ReduxをTypeScriptで実装する中で、useSelector構文内で発生したエラーを解決します。
エラー内容
Property 'hogehoge' does not exist on type 'DefaultRootState'hogehogeは、型DefaultRootStateに存在していないよというエラーです。
該当箇所
下記のcountの箇所でエラーが発生します。
Count.tsx//省略 const Count = () => { const count = useSelector((state) => state.count) //省略 }解決方法
結論:stateの型定義を行いましょう!
1.RootStateという名前でstateの型を定義する
ここではcountだけですが、他にstoreで管理する値がある場合は、それを列挙します。
※RootStateという命名は何でも良いですstore.ts// 省略 export type RootState = { count: number } // 省略2.useSelector関数の引数stateにRootState(型)を定義する
Count.tsx//省略 //①RootStateをインポートする import { RootState } from 'store' const Count = () => { //②stateをRootStateで型定義する const count = useSelector((state: RootState) => state.count) //省略 }これで解決!