- 投稿日:2019-11-28T23:28:15+09:00
React初心者だけどUbuntu18.04上でcreate-react-appとReact Hooksを使ってサクッとカウンタを作る
初投稿です。私もReactの勉強を最近始めたばかりなので備忘録的に。
色々すっ飛ばしてます。まずはUbuntuのバージョン確認
Ubuntu上で作ります。
% cat /etc/os-release NAME="Ubuntu" VERSION="18.04.3 LTS (Bionic Beaver)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 18.04.3 LTS" VERSION_ID="18.04" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=bionic UBUNTU_CODENAME=bionicyarnをインストール
yarn公式に従ってください
https://yarnpkg.com/lang/en/docs/install/#debian-stable$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - $ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list $ sudo apt update && sudo apt install yarncreate-react-appをインストール
これもyarn公式に従ってください
https://yarnpkg.com/lang/en/docs/cli/create/$ yarn global add create-react-appこんな感じのメッセージが出ればOK(多分)。
success Installed "create-react-app@3.2.0" with binaries: - create-react-app Done in 9.51s. $ yarn --version 1.17.3create-react-appコマンドを叩く。
下のコマンドでカレントディレクトリに
my-react-app
というディレクトリが作られる。
my-react-app
の直下にReactでアプリを作るのに必要な諸々のファイルが自動的に作られる。$ create-react-app my-react-app
成功するとこんなメッセージが出る。
Success! Created my-react-app at /home/koralle/learn/my-react-app Inside that directory, you can run several commands: yarn start Starts the development server. yarn build Bundles the app into static files for production. yarn test Starts the test runner. yarn eject Removes this tool and copies build dependencies, configuration files and scripts into the app directory. If you do this, you can’t go back! We suggest that you begin by typing: cd my-react-app yarn start Happy hacking! $ create-react-app --version 3.1.2試しにアプリを起動してみる。
$ cd my-react-app $ yarn startちょっと待つとこんなメッセージになる。
Compiled successfully! You can now view my-react-app in the browser. Local: http://localhost:3000/ On Your Network: http://10.0.2.15:3000/ Note that the development build is not optimized. To create a production build, use yarn build.言われたとおりにブラウザで http://localhost:3000/ にアクセスします。
というか人によっては自動的にブラウザでページが立ち上がります。次からが本題。
カウンタを作るための準備
まずは
my-react-app
直下のディレクトリ構成を確認
今回触るのは./public/index.html
と./src/App.js
だけです。$ cd my-react-app $ ls README.md node_modules package.json public src yarn.lock $ tree ./public ./public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt 0 directories, 6 files $ tree ./src ./src ├── App.css ├── App.js ├── App.test.js ├── index.css ├── index.js ├── logo.svg └── serviceWorker.js 0 directories, 7 files先に
index.html
を開く。今回はできる限り余計なものを全部消します。邪魔なので。
私はこれだけ残しました。<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> <meta name="description" content="Web site created using create-react-app" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <title>React App</title> </head> <body> <div id="root"></div> </body> </html>次は
./src/App.js
を編集。
function App()
全体がいわゆるAppコンポーネントっていうやつですが、
一回こいつを全部消します。
そしてAppコンポーネントをアロー関数式で書き直す。アロー関数式、おしゃれな感じが好きです。import React from 'react'; import logo from './logo.svg'; import './App.css'; const App = () => { } export default App;一回途中経過を見たいので先にカウンタのボタンを作ります。
Appコンポーネント以外はいったん省略します。const App = ()=> { return( <div> <button> + </button> <button> - </button> </div> ); }こんな感じ。小さいけど確かに+ボタンと-ボタンが表示されてます。
次にカウンタを作ります。そのために必要なのはあと最低限以下の二つです。
- カウンタの値を保持する変数 (*1)
- カウンタの値を増減させる関数 (*2)
これらを実装するために、今回はReact HooksというReact公式APIの中のuseState()という関数を利用します。
詳しくは公式ドキュメントを見てください。フック早わかり – React
https://ja.reactjs.org/docs/hooks-overview.html書式は今回の場合こんな感じ。
count
が上記の(*1)で、setCount
は(*2)とイコールではないですが、(*2)の実装に必要になります。
useState(0)
の0はcount
の初期値です。const [count, setCount] = useState(0);これを使って
./src/App.js
にカウンタの動作を書きますが、1行目もちょっと編集します。import React, {useState} from 'react'; import logo from './logo.svg'; import './App.css'; const App = () => { // (*1), (*2)の実装に必要 const [count, setCount] = useState(0); const increment = () => setCount(count+1); // カウンタの値を増やす const decrement = () => setCount(count-1); // カウンタの値を減らす // index.htmlの<div id="root"></div>の中に表示 return( <div> <p> {count} </p> <button onClick={increment}> + </button> <button onClick={decrement}> - </button> </div> ); } export default App;これで完成
- 投稿日:2019-11-28T21:41:59+09:00
僕が開発しやすいReact環境を構築する。
環境
- React
- ESlint
- prettier
- TypeScript
- styled-components
手順
TypeScript用のReactプロジェクトを作成する
yarn create react-app my-app --typescriptsrc内にあるファイルをすべて削除する。
cd my-app cd src # If you're using a Mac or Linux: rm -f * # Or, if you're on Windows: del * # Then, switch back to the project folder cd ..ESlintをインストールする
yarn add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-pluginprettierをインストールする
yarn add -D prettier eslint-config-prettier eslint-plugin-prettier基本設定
.eslintrc.json{ "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended-requiring-type-checking", "plugin:prettier/recommended", "prettier/@typescript-eslint" ], "plugins": [ "@typescript-eslint" ], "parser": "@typescript-eslint/parser", "env": { "browser": true, "node": true, "es6": true }, "parserOptions": { "sourceType": "module", "project": "./tsconfig.json" }, "rules": { // 適当なルール } }VS CODE
setting.js{ // "editor.formatOnSave": false, "eslint.autoFixOnSave": true, "eslint.validate": [ "javascript", "javascriptreact", {"language": "typescript", "autoFix": true }, {"language": "typescriptreact", "autoFix": true } ] }
- editor.formatOnSave: trueは自動整形が衝突するので指定しないでください。
- eslint.validateでtypescriptを指定してください。
- reactを使用するのでtypescriptreactも指定します。
styled-componentsをインストールする
yarn add styled-components yarn add @types/styled-componentsMaterial-UIをインストールする
yarn add @material-ui/coreフォルダを構成する
Styled-componentsの作者の提案する構成にする。
この構成はページやコンポーネント固有のものと複数のコンポーネントから使用される共通コンポーネントに大きくディレクトリを分けるアプローチです。src ├── App │ ├── Header │ │ ├── Logo.js │ │ ├── Title.js │ │ ├── Subtitle.js │ │ └── index.js │ └── Footer │ ├── List.js │ ├── ListItem.js │ ├── Wrapper.js │ └── index.js ├── shared │ ├── Button.js │ ├── Card.js │ ├── InfiniteList.js │ ├── EmojiPicker │ └── Icons └── index.js参考
- 投稿日:2019-11-28T19:43:33+09:00
Reactでhtmlの文字列をレンダリングする
- 投稿日:2019-11-28T10:51:29+09:00
React + Firebaseで認証機能を実装してみよう
Ateam Lifestyle Advent Calendar 2019の2日目は
株式会社エイチームライフスタイル 名古屋開発部のタツミが担当します。
フロントエンドの開発をメインでやっています。
駆け出しのエンジニアですが、よろしくお願いします。はじめに
チームでWebサービスを開発するときにフロントエンドとバックエンドの開発担当に分かれるのはよくある光景ですが、開発のスピードやチームのリソースが求められた時にバックエンドまで開発するのは大変です。
バックエンドのサービス開発に時間を取られずに、フロントエンドの開発に専念したい。
その解決策の1つとして、Googleが提供しているFirebaseというツールの機能の1つを使ってバックエンドの開発をせずに認証機能を作っていきたいと思います。FirebaseはiOS/AndroidアプリからWebサービスまでバックエンドを必要とするサービスで活用することができます。
認証機能の概要
Firebaseではデータベース、ストレージサービス、ホスティングサービス、分析ツール、プッシュ通知などWeb/アプリ開発で活用できる様々なサービスを提供しています。
今回はFirebaseの認証機能の1つであるFirebase Authenticationを使って、Reactで認証機能を実装していきたいと思います。説明の進め方
本記事は題材ごとに全部で以下、4パートに分けて説明します。
Reactの基礎は理解している程で話を進めます。
- Part1: フォームを作成しよう
- Part2: Firebaseの認証機能を有効にしよう
- Part3: ログイン機能を実装しよう
- Part4: ログアウト機能を実装しよう
Part1: フォームを作成しよう
https://firebase.google.com/
↑にアクセスして、Firebaseのアカウントを作成してください。create-react-appで新しいReactプロジェクトを作成してください。
認証用のコンポーネントを作成します。
新たにsrc/components/Auth/index.jsを作成してください。
簡単にメールアドレスとパスワードの基本的なフォームを作成します。※ このファイルに認証機能を全て実装します。実装は例ですので適宜、適切なファイルに実装してください。
src/components/Auth/index.jsimport React, { Component } from 'react' class Auth extends Component { constructor(props) { super(props) this.handleChange = this.handleChange.bind(this) this.handleSubmit = this.handleSubmit.bind(this) } handleChange(event) { // テキストボックスの値を変更する度に、変数に値が格納される this.setState({ [event.target.name]: event.target.value }) } handleSubmit(event) { // 後半のパートでemailとpasswordをfirebaseに登録する処理を追加 event.preventDefault() } render() { return ( <div> <form method="post" onSubmit={this.handleSubmit}> <div> <input type="text" name="email" onChange={this.handleChange} /><br/> <input type="password" name="password" onChange={this.handleChange} /> </div> <button>SUBMIT</button> </form> </div> ) } } export default AuthメールアドレスとパスワードのonChangeイベントに設定したhandleChangeアクションでテキストボックスの値が変わる度にstateに更新された値が格納されます。
後半のパートのなかで、handleSubmitアクションで格納されたstateをFirebase Authenticationに登録していく処理を追加する方法を説明します。
作成した認証コンポーネントを描画するために、App.jsを変更します。
src/App.jsimport React from 'react' import './App.css' import Auth from './components/Auth' function App() { return ( <div className="App"> <Auth /> </div> ) } export default AppPart2: Firebaseの認証機能を有効にしよう
ここからログイン機能を実装していきます。
Part1で作ったFirebaseのアカウントでログインして、コンソールへ移動を選択します。
新しいプロジェクトを作成する必要があるので、プロジェクトを追加を選択してください。
プロジェクト名は何でも良いのですが、今回は認証テストとしてauth-testと命名して続行を選択します。
google analyticsと連携させたい場合は「このプロジェクトでGoogle アナリティクスを有効にする」にチェックを入れますが、今回は連携させないのでチェックを外します。プロジェクトを作成を選択してください。
無事にauth-testプロジェクトが作成されました。
左のメニューからAuthenticationを選択して、ログイン方法を設定を選択してください。様々な認証方法を選ぶことができますが、今回はメールアドレスとパスワードで認証機能を作っていきたいのでメール/パスワードの右の鉛筆マークを選択してください。
メールアドレスとパスワードの認証を有効にします。
今回はパスワードの設定を必須にしたいので、パスワードなしでログインは無効にしておきます。
保存を選択します。以上で、メール/パスワードの設定が有効になりました。
Part3: ログイン機能を実装しよう
Firebase AuthenticationのAPIを使って、フロントエンドにログイン機能を実装します。
認証が完了するとトークンが発行されますが、トークンをstateに保存するとリロードした時にトークン情報が消えてしまうので、localStorageに保存する方法を実装していきます。
Firebase APIと通信する必要があるので、axiosを使っていきます。SignUpとSignIn機能を実装するボタン<button>を設置します。
Part2で作ったフォームの続きから実装していきます。
ボタンをクリックする度に、switchAuthModeHandlerが発火してisSignUpフラグのon, offの切替を行います。src/components/Auth/index.jsimport React, { Component } from 'react' class Auth extends Component { constructor(props) { super(props) this.handleChange = this.handleChange.bind(this) this.handleSubmit = this.handleSubmit.bind(this) this.state = { // signUpとsignInフラグ isSignUp: true } } handleChange(event) { this.setState({ [event.target.name]: event.target.value }) } handleSubmit(event) { event.preventDefault() } // クリックの毎にsignUpとsignInの切替を行う switchAuthModeHandler = () => { this.setState({ isSignUp: !this.state.isSignUp }) } render() { return ( <div> <form method="post" onSubmit={this.handleSubmit}> <div> <input type="text" name="email" onChange={this.handleChange} /><br/> <input type="password" name="password" onChange={this.handleChange} /> </div> <button>SUBMIT</button> </form> {/* signUp、signInボタン */} <button onClick={this.switchAuthModeHandler}> SWITCH TO {this.state.isSignUp ? 'SignIn' : 'SignUp'} </button> </div> ) } } export default Auth続いて、認証メソッドを作成します。
Firebaseの認証機能APIのエンドポイントを紹介します。
APIを使うにはAPIキーを入力する必要があるので、キーを先に確認します。SignUp用のエンドポイントとSignIn用のエンドポイントを確認します。
https://firebase.google.com/docs/reference/rest/auth#section-create-email-password
↑ URLにアクセスします各エンドポイントの?key=[API_KEY]の部分に先ほど確認したAPIキーを入力して使います。
認証メソッドの続きを実装します。
src/components/Auth/index.jsimport React, { Component } from 'react' import axios from 'axios' class Auth extends Component { constructor(props) { super(props) this.handleChange = this.handleChange.bind(this) this.handleSubmit = this.handleSubmit.bind(this) this.state = { isSignUp: true, token: null, error: '' } } handleChange(event) { this.setState({ [event.target.name]: event.target.value }) } handleSubmit(event) { this.auth() event.preventDefault() } switchAuthModeHandler = () => { this.setState({ isSignUp: !this.state.isSignUp }) } auth = () => { // 認証データ const authDate = { email: this.state.email, password: this.state.password, returnSecureToken: true } // signIn用のAPIキー let url = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=APIキーを入力' // signUp用のAPIキー if (this.state.isSignUp) { url = 'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=APIキーを入力' } axios.post(url, authDate) .then(response => { // 返ってきたトークンをローカルストレージに格納する localStorage.setItem('token', response.data.idToken) }) .catch(error => { // Firebase側で用意されているエラーメッセージが格納される this.setState({ error: error.response.data.error.message }) }) } render() { return ( <div> <form method="post" onSubmit={this.handleSubmit}> <div> <input type="text" name="email" onChange={this.handleChange} /><br/> <input type="password" name="password" onChange={this.handleChange} /> </div> <div> {this.state.error} </div> <button>SUBMIT</button> </form> <button onClick={this.switchAuthModeHandler}> SWITCH TO {this.state.isSignUp ? 'SignIn' : 'SignUp'} </button> </div> ) } } export default Authこれでログイン機能の実装は完了です。
サインアップすると新しくユーザが作られているのを確認することができます。
パスワードは6文字以上でないと登録できないので注意してください。サインアップまたは、サインインするとlocalStrageにtokenが保存されているのを確認することができます。
(localStorage.getItem('token'))
でtokenが保存されている(ログインしている)場合に任意の処理を実行できます。if (localStorage.getItem('token')) { // ログインしている場合に任意のメソッドを実行 }Part4: ログアウト機能を実装しよう
ログアウトは簡単です。
ログインの時にlocalStrageに保存したtokenを削除するだけです。src/components/Auth/index.jsimport React, { Component } from 'react' import axios from 'axios' class Auth extends Component { constructor(props) { super(props) this.handleChange = this.handleChange.bind(this) this.handleSubmit = this.handleSubmit.bind(this) this.state = { isSignUp: true, token: null, error: '' } } handleChange(event) { this.setState({ [event.target.name]: event.target.value }) } handleSubmit(event) { this.auth() event.preventDefault() } switchAuthModeHandler = () => { this.setState({ isSignUp: !this.state.isSignUp }) } auth = () => { const authDate = { email: this.state.email, password: this.state.password, returnSecureToken: true } let url = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=APIキーを入力' if (this.state.isSignUp) { url = 'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=APIキーを入力' } axios.post(url, authDate) .then(response => { localStorage.setItem('token', response.data.idToken) }) .catch(error => { this.setState({ error: error.response.data.error.message }) }) } logout = () => { // トークンの削除 localStorage.removeItem('token') } render() { return ( <div> <form method="post" onSubmit={this.handleSubmit}> <div> <input type="text" name="email" onChange={this.handleChange} /><br/> <input type="password" name="password" onChange={this.handleChange} /> </div> <div> {this.state.error} </div> <button>SUBMIT</button> </form> <button onClick={this.switchAuthModeHandler}> SWITCH TO {this.state.isSignUp ? 'SignIn' : 'SignUp'} </button><br/> {/* ログインしている場合だけログアウトボタンが表示される */} {localStorage.getItem('token') && <button onClick={this.logout}>LOGOUT</button>} </div> ) } } export default Auth実装後の画面イメージ
・ サインイン、サインアップするとlocalStrageにトークンが保存される
・ リロードしてもトークンは保持し続ける
・ ログアウトを選択するとトークンが削除されるまとめ
長くなりましたが、いかがでしたでしょうか?
バックエンドサービスを何らかのフレームワークを使って実装していれば、標準の認証機能を導入できるとは思いますが、自前でデータベースを用意したり、個人情報をどう管理するかを考える必要があります。
Firebase Authenticationを使うことでこれらの問題を意識することなく、認証機能を実装することができました。
Firabaseでは、導入部分でも紹介したように様々な機能が用意されているので、ぜひ活用してみてください。Ateam Lifestyle Advent Calendar 2019の3日目は、@chardenがお送りします!!どんなネタを用意してくるのか楽しみです!!
"挑戦"を大事にするエイチームグループでは、一緒に働けるチャレンジ精神旺盛な仲間を募集しています。興味を持たれた方はぜひエイチームグループ採用サイトを御覧ください。
https://www.a-tm.co.jp/recruit/
- 投稿日:2019-11-28T09:37:30+09:00
Gatsby + ContentfulをTypeScript化する
Gatsby + Contentfulという構成にすることで、CMSのフロントエンドとバックエンドを分離することが可能になります。
ただ、この構成にTypeScriptを導入する場合、一手間必要になります。その手順を書いていきます。
また、GatsbyのTypeScript化については、下記の記事が大変参考になりました。内容が被る箇所が多々あるので、その部分についてはこの記事では割愛します。
Gatsby.js を完全TypeScript化するGatsbyアプリ作成
まず、gatsby-cliをインストールします。
yarn global add gatsby-cliその後、下記コマンドを実行することでgatsbyの雛形がダウンロードされます。
gatsby new <任意のディレクトリ名>続けて以下のコマンドを実行し、localhost:3000でサイトが表示されればOK。
cd <任意のディレクトリ名> yarn developContenfulのほうもアカウント登録して、適当なSpaceを作成しておきます。
GatsbyのTypeScript化
GatsbyのTypeScript化については、冒頭で紹介したこちらの記事を参考にしてください。自分が書くよりも確実にわかりやすいはず...!
Contentfulの導入
GatsbyとContentfulを繋げるには、プラグインを2つインストールする必要があるので、以下のコマンドを実行します。
yarn add gatsby-source-contentful gatsby-transformer-remarkgatsby-source-contentfulは、ContentfulからGraphQLでデータを取得できるようにするプラグイン。これを導入することでデータ取得用のQueryが利用できるようになります。
gatsby-transformer-remarkは、Markdownで記述されたテキストをHTMLに変換してくれるプラグインです。これを導入することで、Contentful側でMarkdownで書いた内容を、Gatsby側でそのままHTMLとして表示できます。
また、インストールしたプラグインはgatsby-config.jsに記述しないと有効にならないので、忘れないように注意です。
gatsby-config.jsplugins: [ ..., `gatsby-transformer-remark`, { resolve: `gatsby-source-contentful`, options: { spaceId: process.env.CONTENTFUL_SPACE_ID, accessToken: process.env.CONTENTFUL_ACCESS_TOKEN, }, }, ]spaceIdとaccessTokenはContentfulから取得します。envの参照方法はお好みで。自分はdirenvを使いました。
ここまでできたら、Contentful側で適当なコンテンツを作成しておきます。また、GraphiQLで、Contentfulからデータが取得できることを確認しておきましょう。
gatsby-node.jsをTypeScript対応させる
gatsby-node.jsは、主に動的なサイトページを作る際に変更するファイルです(/posts/:idのような)
また、動的なページテンプレートは、src/templates配下に配置します。gatsby-node.jsでContentfulからデータを取得し、そのデータをテンプレートファイルに流し込み、ページを作るイメージです。
今回やりたいのは、gatsby-node.jsで取得したデータ構造を型(Type)としてexportし、テンプレートファイルでそれをimportして紐づけることです。これにより、テンプレート側で型推論を利用したコーディングが可能になります。
参考にした記事に倣って、gatsby-node.jsに以下を記述します。(ts-nodeをインストールしておく必要があります)
gatsby-node.js'use strict' require('ts-node').register({ compilerOptions: { module: 'commonjs', target: 'esnext', }, }) exports.createPages = require('./gatsby-node/index').createPagesこれにより、./gatsby-node/index.tsが実質的なエントリーポイントになりました。TypeScriptを用いてgatsby-nodeファイルを記述していくことができます。
ContentfulからPost(投稿)を取得して、動的にページを割り当てるための記述は以下のようになります。
index.tsconst path = require("path") import { GatsbyNode } from "gatsby" import { ContentfulPostConnection, ContentfulPost, } from "../types/graphql-types" // GraphQLにより取得されるデータの型 type Result = { allContentfulPost: ContentfulPostConnection } // テンプレートファイルに渡すデータの型 export type PostContext = { post: ContentfulPost } // 実行するGraphQLのQuery const query = ` { allContentfulPost { edges { node { content { childMarkdownRemark { html } content } publishedAt slug title } } } } ` // 動的にページを生成する関数 export const createPages: GatsbyNode["createPages"] = async ({ graphql, actions: { createPage }, }) => { // ジェネリクスでGraphQLの返却データ型を指定 const result = await graphql<Result>(query) const { edges } = result.data.allContentfulPost // 利用するテンプレートファイルを指定 const postTemplate = path.resolve("./src/templates/post.tsx") edges.forEach(edge => { // ジェネリクスでcontextプロパティ(テンプレートに渡すデータ)の型を指定 createPage<PostContext>({ path: `/posts/${edge.node.slug}`, component: postTemplate, context: { post: edge.node }, }) }) }ContentfulPostConnectionやContentfulPostといった型は、gatsby-plugin-graphql-codegenというプラグインを導入後、buildすることで自動的に生成されるものです。
GraphQLでContentfulからデータを取得する際のQueryに対応する型が全て自動的に生成されるので、これを用いて型を指定していきます。大量にあるので目当ての型を探すのも一苦労ですが、頑張って探します。
await graphql<Result>(query)
で、GraphQLで取得できるデータの型を指定しています。上記のquery(allContentfulPost {...})ではこういったデータが返却されてきます。(GraphiQLでの実行結果です){ "data": { "allContentfulPost": { "edges": [ { "node": { "publishedAt": "2019-11-27T00:00+09:00", "slug": "sample", "title": "sample", "content": { "childMarkdownRemark": { "html": "<p>sample content</p>" } } } } ] } } }これに対応する、ContentfulPostConnectionの型はこんな感じ。
export type ContentfulPostConnection = { totalCount: Scalars['Int'], edges: Array<ContentfulPostEdge>, nodes: Array<ContentfulPost>, pageInfo: PageInfo, distinct: Array<Scalars['String']>, group: Array<ContentfulPostGroupConnection>, };テンプレートファイルとの型による紐付けは、
createPage<PostContext>
で行なっています。こう書くことで、以下のcontextの部分をPostContextで縛ることができます。createPage<PostContext>({ path: `/posts/${edge.node.slug}`, component: postTemplate, context: { post: edge.node }, })contextはテンプレートに渡す値を指定している箇所になるので、ここをPostContextで縛ったことにより、テンプレート側に渡される変数も当然PostContext型ということになります。
post.tsimport React from "react" import Layout from "../components/layout" import { PostContext } from "../../gatsby-node" type Props = { pathContext: PostContext } // pathContextがgatsby-node.tsから渡される変数で、PostContext型となる const Post: React.FC<Props> = ({ pathContext }) => { const contentHtml = pathContext.post.content.childMarkdownRemark.html return ( <Layout> <div dangerouslySetInnerHTML={{ __html: contentHtml }} /> </Layout> ) } export default Postこれにより、テンプレートファイルでも型推論を利用したコーディングが可能になりますね。
まとめ
自動生成される型定義が多すぎて、目当ての型を探すのに一番苦労しました。ここまでやってしまえば、あとはTypeScriptの恩恵を受けながら開発していけるのかなと思います。
- 投稿日:2019-11-28T01:08:29+09:00
[React]説明がいらないReact+TypeScript開発環境設定(CRA無し)
Install
yarn init --yes yarn add next react react-dom yarn add @zeit/next-typescript @types/next @types/react @zeit/next-typescript @types/next @types/react
ファイル追加
// next.config.js const withTypescript = require('@zeit/next-typescript') module.exports = withTypescript()//.babelrc { "presets": ["next/babel", "@zeit/next-typescript/babel"] }//tsconfig.json { "compilerOptions": { "allowJs": true, "allowSyntheticDefaultImports": true, "jsx": "preserve", "lib": ["dom", "es2017"], "module": "esnext", "moduleResolution": "node", "noEmit": true, "noUnusedLocals": true, "noUnusedParameters": true, "preserveConstEnums": true, "removeComments": false, "skipLibCheck": true, "sourceMap": true, "strict": true, "target": "esnext" } }eslint setting
npm i eslint -D npm i @typescript-eslint/parser -D npm i @typescript-eslint/eslint-plugin -D npx eslint --init
//.eslintrc.js module.exports = { "env": { "browser": true, "es6": true, "node": true }, "extends": [ "eslint:recommended", "plugin:react/recommended" ], "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly" }, "parserOptions": { project: './tsconfig.json', //추가 "ecmaFeatures": { "jsx": true }, "ecmaVersion": 2018, "sourceType": "module" }, "plugins": [ "react" ], "rules": {} };package.json
"scripts": { "dev": "next", "build": "next build", "start": "next start" }pages/index.tsx 追加
import React from "react"; export default () => { return <div>hello</div>; };yarn add --dev typescript @types/node