- 投稿日:2020-04-26T19:53:36+09:00
Nextjsでビルド時ではなく、アプリ起動時に環境変数を読み込ませる方法
概要
Nextjsでビルド時ではなく、アプリ起動時に環境変数を設定する方法を調べたのでメモとして残している。
next.config.js
の設定以下のように設定する。
// .envファイルから読み込むように設定している。 require("dotenv").config(); module.exports = { // ビルド時に利用できる環境変数 env: { STEP: process.env.MY_STEP, }, // 実行時かつ、サーバサイドで利用できる serverRuntimeConfig: { MY_SECRET: process.env.MY_SECRET, }, // 実行寺かつ、サーバサイド、クライアントサイドのどちらでも利用できる publicRuntimeConfig: { API_ENDPOINT: "/my/api/version/1", }, };
next.config.js
で設定した値を読み込む方法例:
pages/index.js
で読み込む場合import getConfig from "next/config"; // next.config.jsで設定した値を取得する const { serverRuntimeConfig, publicRuntimeConfig } = getConfig(); // クライアントサイド: API_ENDPOINTは参照できるが、MY_SECRETは参照不可になっている。 export default function Index(props) { return ( <div> API_ENDPOINT: {publicRuntimeConfig.API_ENDPOINT} MY_SECRET: {serverRuntimeConfig.MY_SECRET} <pre>{JSON.stringify(props, null, 4)}</pre> </div> ); } // サーバサイド: MY_SECRETとAPI_ENDPOINTはどちらも参照可能 export const getServerSideProps = () => { return { props: { MY_SECRET: serverRuntimeConfig.MY_SECRET, API_ENDPOINT: publicRuntimeConfig.API_ENDPOINT, }, }; };
- 投稿日:2020-04-26T19:51:53+09:00
Next.js と Typescript で React-StyleGuidist を使うための設定
概要
Next.jsとTypeScriptでReact-StyleGuidistを利用したかったので調査したときのメモとして残している。
Next.js プロジェクトの作成
create-next-app [prject-name]cd [project-name] mkdir src mv pages src/ mkdir src/components mkdir src/styleguide必要なパッケージのインストール
# NextjsでTypeScriptを使うためのパッケージ yarn add -D @types/node typescript @types/react # React-StyleguidistをNextjs,TypeScriptと一緒に使うためのパッケージ yarn add -D react-styleguidist babel-loader react-docgen-typescript # Bootstrapを使いたかったので追加しているが、必須ではない。 yarn add bootstrap jquery popper.js
styleguide.config.js
の設定const path = require("path"); module.exports = { components: "./src/components/**/*.tsx", propsParser: require("react-docgen-typescript").withCustomConfig( "./tsconfig.json" ).parse, styleguideComponents: { Wrapper: path.resolve(__dirname, "src/styleguide/Wrapper.tsx"), }, webpackConfig: { module: { rules: [ { test: /\.tsx?$/, exclude: /node_modules/, loader: "babel-loader", }, { test: /\.css?$/, use: ["style-loader", "css-loader"], }, ], }, }, };
src/styleguide/Wrapper.tsx
の設定import React from "react"; import "bootstrap/dist/css/bootstrap.min.css"; import "bootstrap"; const Wrapper: React.FC = ({ children }) => { return <div>{children}</div>; }; export default Wrapper;
babel.config.js
の設定module.exports = { presets: ["next/babel"], plugins: [], };
package.json
の設定"scripts": { "dev": "next dev", "build": "next build", "start": "next start", "styleguide": "styleguidist server", "styleguide:build": "styleguidist build" },StyleGuistサーバの起動
あとはReactで利用する場合と同じように、
以下のコマンドで起動し、src/components
にコンポーネントと対応するMDファイルを作成していく。yarn styleguide
- 投稿日:2020-04-26T19:50:33+09:00
【React入門】Reactとは
初めに
フロントエンド開発初心者がReactの案件に参画することになったため、チュートリアル等で勉強し得た知識をまとめてみました。
Reactとは
Reactはユーザーインターフェース(UI)を構築するためのJavaScriptライブラリです。
UIを「コンポーネント」という小さく独立した部品を組み合わせて構築することができます。Reactの使用例
実際にReactを使ったサンプルアプリを見てみましょう。
以下は押されたボタンによって値を増減させる簡単なカウンターアプリです。See the Pen Counter by Amsel (@amsel1676) on CodePen.
サンプルアプリclass Counter extends React.Component{ constructor(props){ super(props); this.state = { count: 0, }; } render(){ return ( <div> <div className="title">Counter</div> <button className="minusButton" onClick={() => this.setState({count: this.state.count-1})} > - </button> <div className="counter">{this.state.count}</div> <button className="plusButton" onClick={() => this.setState({count: this.state.count+1})} > + </button> </div> ); } } ReactDOM.render( <Counter />, document.getElementById('app') );Counterコンポーネント
Counterコンポーネントclass Counter extends React.Component{ constructor(props){ super(props); this.state = { count: 0, }; } render(){ return ( <div> <div className="title">Counter</div> <button className="minusButton" onClick={() => this.setState({count: this.state.count-1})} > - </button> <div className="counter">{this.state.count}</div> <button className="plusButton" onClick={() => this.setState({count: this.state.count+1})} > + </button> </div> ); } }上のコードの
Counter
がReactコンポーネントです。
Counter
コンポーネントはprops
というパラメータを受け取り、constructor
メソッドで値を初期化させ、render
メソッドで表示するビューの説明書である仮想DOMを返却します。
Counter
コンポーネントはstate
というオブジェクトでアプリケーションの状態を保持して、状態が変わるごとにその差分を再レンダリングする仕組みになっています。この状態管理の仕組みにより、上のサンプルアプリではボタンを押した結果がすぐに画面に描画されるようになっています。
コンポーネント呼び出し
コンポーネント呼び出しReactDOM.render( <Counter />, document.getElementById('app') );上記がコンポーネントの呼び出し部分となります。
ReactDOM.render
メソッドはCounter
コンポーネントが返却した仮想DOMを指定したDOMにレンダリングします。JSX
上のコードはJavaScriptとHTMLタグが入り混じったような特徴的な見た目をしていますが、これはJSXを用いて書かれているためです。
JSXとは「JavaScript XML」の略で、XML風に作られたJavaScriptの拡張シンタックスです。
JSXのメリット
JSXはあくまで拡張シンタックスであり、JSXで書かれたコードはBabelを通じてJavaScriptの関数呼び出しへと置き換えられます。
つまりReactはJSXを使用せずに書くことが可能です。
JSXを使用せずに書くと以下のようになります。JSXなしfunction Welcome(props){ return React.createElement( 'h1', null, `Welcome ${props.name}`, ); } ReactDOM.render( <Welcome name="Tom" />, document.getElementById('app') );同様のコードをJSXを使用して書くと以下のようになります。
JSXありfunction Welcome(props) { return <h1>Welcome, {props.name}</h1>; } ReactDOM.render( <Welcome name="Tom" />, document.getElementById('app') );このようにJSXを使用することでHTML風に構造が可視化され、可読性が向上します。
参考
- 投稿日:2020-04-26T17:25:12+09:00
【React/laravel】componentDidMount()でsetStateする予定のデータを利用してComponentのStateを定義すると失敗する【小説印刷支援サイト】
承前
小説を簡単に同人誌の形にできる印刷できるWebサイトを作りたいと思っている。
今回はその前段階として、印刷する際のプレビュー画面を作成していたときに躓いた話。事象
直前まで動いていたReactコンポーネントが急にエラーを吐き始めた。
Uncaught TypeError: Cannot read property 'chapterNumber' of undefined at RenderUi (index.js:70616) at renderWithHooks (index.js:48209) at mountIndeterminateComponent (index.js:50888) at beginWork (index.js:52002) at HTMLUnknownElement.callCallback (index.js:33594) at Object.invokeGuardedCallbackDev (index.js:33643) at invokeGuardedCallback (index.js:33698) at beginWork$1 (index.js:56609) at performUnitOfWork (index.js:55560) at workLoopSync (index.js:55536)
chapterNumber
はChapterContent
のメンバ。
ChapterContent
はNovelWithContents
のメンバ。調査
ChapterContent
のインスタンスが空だった。
なんならNovelWithContents
のインスタンスも空だった。
('、3_ヽ)_やりたかったことと原因
NovelWithContents
のインスタンスは、componetnDidMount()でPHPから持ってきた値を詰めていた。
Console.log()を仕込んでみたところ、componentnDidMount()を通っていないことが分かった。
よって、NovelWithContents
は空で然るべきということが分かった。axiosで取得したテキストデータに前処理をしてReactコンポーネントに渡したかった。
その前処理の部分で、データが無いクラスのプロパティを参照してエラーを吐いていた。componentDidMount() { console.log(this.state.novelId+"sssssssssssssssssssssssss"); axios .get(/*API CALL*/) .then(response => { let data: NovelWithContents = response.data; console.log(response.data); this.setState({ contentsJson: data }); }) .catch(() => { console.log("通信に失敗しました。") }); }対処
失敗した方法
constructor
のstate
初期化後にaxios
以下を移してもうまく行かなかった。
render
に移しても同様。うまく行った方法
色々やった。
- 前処理後の値をStateにして初期値を与えた。
- 初回のRender時、即ちaxiosが間に合ってない時に空でエラーを吐かなくなった。
- 初期化処理をaxiosの
finally()
処理に集めた。
- これによりデータ取得と初期化処理の順番の整合性を取りながら、非同期処理という体は崩さずに済んだ。
- 上記を可能にするために全体的にリファクタリング
componentDidMount() { axios .get(/*API CALL*/) .then(response => { let data: NovelWithContents = response.data; console.log(response.data); this.setState({ contentsJson: data }); }) .catch(() => { console.log("通信に失敗しました。") }).finally(()=>{ let hoge=""; //前処理。 }); }結局3時間くらいハマってしまった。
反省
- Reactへの理解度が浅い中で始めたProjectだったので、設計はあとでキチンとやりなおす。
今回実現した機能
小説のページング
小説印刷支援サイト
やや遅れ気味。7月リリースを目指したい。
- 投稿日:2020-04-26T17:25:12+09:00
【React/laravel】axiosによる非同期のAPI callの結果を待ってからStateに値を入れたかった【小説印刷支援サイト】
承前
小説を簡単に同人誌の形にできる印刷できるWebサイトを作りたいと思っている。
今回はその前段階として、印刷する際のプレビュー画面を作成していたときに躓いた話。事象
直前まで動いていたReactコンポーネントが急にエラーを吐き始めた。
Uncaught TypeError: Cannot read property 'chapterNumber' of undefined at RenderUi (index.js:70616) at renderWithHooks (index.js:48209) at mountIndeterminateComponent (index.js:50888) at beginWork (index.js:52002) at HTMLUnknownElement.callCallback (index.js:33594) at Object.invokeGuardedCallbackDev (index.js:33643) at invokeGuardedCallback (index.js:33698) at beginWork$1 (index.js:56609) at performUnitOfWork (index.js:55560) at workLoopSync (index.js:55536)
chapterNumber
はChapterContent
のメンバ。
ChapterContent
はNovelWithContents
のメンバ。調査
ChapterContent
のインスタンスが空だった。
なんならNovelWithContents
のインスタンスも空だった。
('、3_ヽ)_やりたかったことと原因
やりたかったこと
NovelWithContents
がなぜ空なのかaxiosで取得したJSONモデルデータが詰まっている予定だったが、ログを出してみたら問題が分かった。
*componentDidMount()
はComponent描画後に呼ばれるため、Component描画時にココで取得する予定の値を参照してしまうとエラーになる。その後、もう一つの問題も分かった。
* axiosは非同期処理をするので、Constructorなどで事前に値を取得しようとしてもデータ取得処理が間に合うかどうか分からない(間に合わない)componentDidMount() { console.log(this.state.novelId+"sssssssssssssssssssssssss"); axios .get(/*API CALL*/) .then(response => { let data: NovelWithContents = response.data; console.log(response.data); this.setState({ contentsJson: data }); }) .catch(() => { console.log("通信に失敗しました。") }); }結論
前処理の部分で、データが無いクラスのプロパティを参照してエラーを吐いていた。
対処
失敗した方法
constructor
のstate
初期化後にaxios
以下を移してもうまく行かなかった。
render
に移しても同様。うまく行った方法
色々やった。
- 前処理後の値をStateにして初期値を与えた。
- 初回のRender時、即ちaxiosが間に合ってない時に空でエラーを吐かなくなった。
- 初期化処理をaxiosの
finally()
処理に集めた。
- これによりデータ取得と初期化処理の順番の整合性を取りながら、非同期処理という体は崩さずに済んだ。
- 上記を可能にするために全体的にリファクタリング
componentDidMount() { axios .get(/*API CALL*/) .then(response => { let data: NovelWithContents = response.data; console.log(response.data); this.setState({ contentsJson: data }); }) .catch(() => { console.log("通信に失敗しました。") }).finally(()=>{ let hoge=""; //前処理。 }); }結局3時間くらいハマってしまった。
反省
- Reactへの理解度が浅い中で始めたProjectだったので、設計はあとでキチンとやりなおす。
今回実現した機能
小説のページング
小説印刷支援サイト
やや遅れ気味。7月リリースを目指したい。
- 投稿日:2020-04-26T17:16:57+09:00
サイト構築のタスクを自動化する「Gatsby Recipes」とタスク記述言語としてのMDX
2020年4月、Reactでウェブサイトやアプリケーションを作るGatsbyから「Gatsby Recipes」の発表がありました。(執筆時点ではまだExperimentalな機能です)
?New! "Announcing #GatsbyRecipes"
— Gatsby (@gatsbyjs) April 16, 2020
?????? Recipes teach users how to accomplish desired tasks in Gatsby while also automating the process.
➡️ https://t.co/X0ueTQHqv4
✨ This release is the first step towards adding a lot more automation capabilities to Gatsby. pic.twitter.com/sBH4EVK4CzGatsby RecipesはGatsbyでサイトを制作する際に利用するようなタスクを自動化するものです。上記のツイートのようにCLIを経由して使います。
次のようなタスクを自動化します。
- ページやレイアウトの作成
- プラグインのインストールと設定
- TypeScriptの設定
利用するにはGatsbyプロジェクト内で次のコマンドを入力しgatsby, gatsby-cliを最新のものにしてください。
npm install -g gatsby-cli@latest npm install gatsby@latestインストールを終えたら、
gatsby recipes
と入力することで、利用できるレシピを選択できます。
前もって、利用したいレシピが決まっている場合gatsby recipes styled-components
のように指定が可能です。レシピを決定すると、ウィザードに従って操作を行うとレシピが実行されます。ライブラリのインストールやgatsby-configの書き換え、必要なファイルの生成などを行ってくれます。
MDXを使ったタスクの記述
Gatsby Recipesは単にサイト構築の支援ツールのように見えますが、一番おもしろいところは上記のようなタスクを記述するのにMDX(Markdown + JSX)を利用しているところです。
MDXはMarkdown内でJSXを仕様できるようにしたフォーマットです。
MDXを使った代表的なものにjxnblk/mdx-deckというスライドツールがあります。MarkdownとJSXを利用してプレゼンテーションを作っていくツールです。冒頭のツイートのレシピは次のようなMDXを元に動作しています。
# Setup Theme UI This recipe helps you start developing with the [Theme UI](https://theme-ui.com) styling library. <Config name="gatsbyjs/add-theme-ui" /> --- Install packages. <NPMPackage name="theme-ui" /> <NPMPackage name="gatsby-plugin-theme-ui" /> <NPMPackage name="@theme-ui/presets" /> --- Add the plugin `gatsby-plugin-theme-ui` to your `gatsby-config.js`. <GatsbyPlugin name="gatsby-plugin-theme-ui" /> --- Write out Theme UI configuration files. <File path="src/gatsby-plugin-theme-ui/index.js" content="https://gist.github.com/KyleAMathews/ab59e200e4f8a1b4109dddb51b2140f9/raw/209a2e7c589766869522b12f7f6cecaf3f7a6f81/index.js" /> <File content="export default {}" path="src/gatsby-plugin-theme-ui/components.js" /> --- **Success**! You're ready to get started! - Read the docs: https://theme-ui.com - Learn about the theme specification: https://system-ui.com *note:* if you're running this recipe on the default starter (or any other starter with base css), you'll need to remove the require to `layout.css` in the `components/layout.js` file as otherwise they'll override some theme-ui styles.見てわかる通り、Markdownで記述した箇所が文章として、JSXで表示されている部分が実際に実行されている構造になります。
以前からタスク自動化のツールはありましたが、人間に優しくない記述や自由度が低いものが多く存在しました。その課題をうまく解決しようとしている点でGatsby Recipesは非常に面白い試みでしょう。Recipesに見るNext.jsとの棲み分け
ここからは自分の見解になります。
つい最近、同じくReactをベースにしたフレームワークのNext.js 9.3が公開されました。Next.js 9.3ではStaticにデータを扱う部分が強化され、一見してGatsbyとNext.jsの差が小さくなっています。
しかし、Next.jsは大体何でも作れる万能ナイフ的なフレームワークである一方、Gatsbyは(アプリケーションも作れますが)ウェブサイトに特化したフレームワークです。ウェブサイトに特化している分、プラグインやスターター(テンプレート)が使いやすくエコシステムも強大です。Gatsby Recipesは一般的なタスクに対応し、よりGatsbyのセットアップは簡単になっていきます。また、RFC段階ではありますが、GatsbyのサイトをいじるためのUI「Gatsby Admin」の動きもあります。これは何でも作れるNext.jsには作りにくく、ウェブサイトに特化したGatsbyだから出来る取り組みです。
Gatsbyは登場して5年ほどたって成熟しているような印象も受けますが、よりよいDXで開発が出来るような方向でさらなる進化をしています。
興味を持った方はGatsby Reciepesはもちろん、Gatsby Adminへ目を向けてみてはいかがでしょうか?
- 投稿日:2020-04-26T11:00:28+09:00
ことばを学ぶ LEARN GATSBY 週間 #2日目
こんにちは !
今日はレイアウト
コンポーネント
について、学びたいと思います.レイアウトとコンポーネント
src
内にcomponents
フォルダとlayouts
フォルダを作成します.
src
内には、これで3つのフォルダ (components
,layouts
,pages
) が存在することになります.
components
フォルダには、ヘッダーやフッター、サイドバーなどの部品 (コンポーネント)
を入れます.
layouts
フォルダには、ページのレイアウト
を入れます.部品を組み込んだレイアウトをページで使うことによって、コードが見やすくなるため、管理しやすくなります. また一度作ったコンポーネントは使いまわすことができます.
ヘッダーを作る
components
フォルダ内に、header.js
というヘッダーコンポーネントを作成します.
中身を以下のようにします.header.jsimport React from 'react' import { Link } from 'gatsby' const Header = () => { return ( <div> <nav> <ul> <li> <Link to='/'>Home</Link> </li> <li> <Link to='/bloglist'>BlogList</Link> </li> <li> <Link to='/about'>About</Link> </li> </ul> </nav> </div> ) } export default Headerレイアウトを作る
まず最初に
layouts
フォルダにcommon.js
ファイルを追加します.
中身を以下のようにします.common.jsimport React from 'react' import Header from '../components/header' const Layout = (props) => { return ( <div> <Header /> {props.children} </div> ) } export default Layout
Layout
を使ってindex.js
about.js
の中身を少し変えてみましょう.index.jsimport React from 'react' import Layout from '../Layouts/common' const Home = () => { return ( <Layout> <h1>This is Home </h1> <h2>Some contents coming soon ...</h2> </Layout> ) } export default Homeabout.jsimport React from 'react' import Layout from '../Layouts/common' const About = () => { return ( <Layout> <h1>This is About Page </h1> <p>About Page coming soon ...</p> </Layout> ) } export default About開発用サーバーに移動して (
gatsby develop
) 、どうなっているのか確認してみましょう.
Home
リンク、About
リンクを押してみます. Layout であるcommon.js
内の ヘッダーコンポーネント<Header/>
の部品は変化せず、{props.children}
という要素が変化していることがわかります.
index.js
About.js
内の<Layout>
で囲まれた部分がcommon.js
内の{props.children}
に挿入されているわけです.
BlogList
リンクを押してみると、なにやらエラーが表示されるようです.
pages
ディレクトリの中にbloglist.js
ファイルを追加してみましょう.
このブログリストページ
を記事一覧ページにしたいと思います !ここで一つ問題になってくるのが、どうやって記事のデータを取ってくるのかということです.
Gatsby
ではGraphQL
を利用してデータを簡単に取得することができます.まず、データを扱うための便利な
Gatsby
にプラグインの設定をしてみましょう.gatsby-config.jsの設定
gatsby-config.js
では、gatsby
が提供している様々なプラグインを設定することができます.
現在のディレクトリのどこかに、すべての記事のデータを置いておき、このgatsby-config.js
にて、そのパスを指定するだけでデータをGraphQL
で参照することができます.
ここでは、記事はマークダウン
ファイルとしてあります.実際にやってみましょう. 全記事のデータはこちらからダウンロードして
my-blog
ディレクトリに置いてください. (content
という名前です)
gatsby-config.js
を開いて、以下のように編集してください.gatsby-config.jsmodule.exports = { /* Your site config here */ plugins: [ 'gatsby-plugin-sass' , { resolve: 'gatsby-source-filesystem' , options: { name: 'content' , path: `${__dirname}/content/` } }, 'gatsby-transformer-remark', ], }詳しく見てみましょう.
gatsby-plugin-sass
を入れると、スタイルにSASS/SCSS
を利用することができるようになります. これはとても便利なので、今のうちに入れておきます.
gatsby-source-filesystem
によって、content
ディレクトリ内のデータを取得し、
gatsby-transformer-remark
によって、取得したマークダウン
ファイルをgraphQL
にて認識できるような形に変換します.プラグインの設定はこれで終わりです.
実際に、プラグインをインストールしてみます.
npm install --save gatsby-source-filesystem node-sass gatsby-plugin-sass gatsby-transformer-remarkGraphQLを使ってみる
my-blog
内に.env.development
ファイルを作成し、その中身を以下のようにします.GATSBY_GRAPHQL_IDE=playgroundターミナルで
env-cmd
をインストールします.npm install --save-dev env-cmd
package.json
ファイル内の"develop"
のところを以下のように編集します."develop": "env-cmd -f .env.development gatsby develop",そして、
npm run develop
にて開発用サーバーを立ち上げます.
http://localhost:8000/___graphql
にアクセスしてみましょう !
クールなUI
を持ったGraphQL Playground
が現れました.左側の画面に以下を打ち込んでください
query { allMarkdownRemark { edges { node { frontmatter { title } } } } }真ん中の三角ボタンを押すと、右側の画面に、
contnent
内のマークダウン
ファイルの情報が示されます. いま、content
内にあるblog
フォルダには6
つの記事があるので、右側の画面には、6
つの記事の情報 (タイトル) がずらーと示されました.このように、
Gatsby
では簡単に、記事の情報を引き出すことができるんです.まとめ
Layout
とcomponent
を使うことによってコードが見やすくなり、再利用することができるGraphQL
を利用することで、データを簡単に引き出すことができる次回予告
次回は、
GraphQL
を利用して、今回引き出したデータを実際にブログリストページ
に、表示させたいと思います.ここまで読んでくださって、ありがとうございました.
- 投稿日:2020-04-26T03:27:33+09:00
React内でsetIntervalを実現する (LiveScript版)
ReactでsetIntervalが効かなくて困ったので調査
スマートな解決方法が載っている記事がヒットしたので備忘録として残しておく。参考記事: Making setInterval Declarative with React Hooks
数時間以上React Hooksを使っていたら、
おそらく興味をそそられる問題にぶつかったことでしょう。
setIntervalを使っても思ったようにはいかないのです。という訳でこの記事のコードをLiveScriptで書き直したのがこちら。
JavaScriptのコードは上記の記事を参考。functions/use-interval.lsrequire! { react: {use-ref, use-effect} } module.exports = (cb, delay) !-> saved-cb = use-ref! use-effect do -> saved-cb.current = cb [cb] use-effect do -> if delay isnt null id = set-interval _, delay <| -> saved-cb.current! return -> clear-interval id [delay]