- 投稿日:2020-04-21T22:11:55+09:00
react-redux-firebase を導入しようとしたときに起きた@@reactReduxFirebase/LOGINのエラーを消したメモ
概要
- react-redux-firebase を使ってCRUD を試してみた
- 一応動いた。。。が、使いこなせてない感。
- そのまま導入すると、以下のエラーが発生。
index.js:1 A non-serializable value was detected in an action, in the path: `auth`. Value: Take a look at the logic that dispatched this action: {type: "@@reactReduxFirebase/LOGIN", auth: P, preserve: undefined}
- 以下のようにgetDefaultMiddlewareのオプションを指定することででエラーを消すことができた。
import { configureStore, getDefaultMiddleware, EnhancedStore, } from '@reduxjs/toolkit' import { rootReducer } from './rootState' import logger from 'redux-logger' export const setupStore = (): EnhancedStore => { const middlewares = [ ...getDefaultMiddleware({ + serializableCheck: { + ignoredActions: ['@@reactReduxFirebase/LOGIN'], + }, }), ]参考
React + Redux + Firebase を使ってログイン機能あり掲示板アプリ開発②
Docker で zeit nowのデプロイ環境とNext.jsの開発環境を作ったメモ
PdfMakeを使ったPDF作成APIをZeit Nowで動かしたメモ
react-redux-firebase
react-redux-firebase useFirestore.md
error @@login
firestore
Zeit Now + Next.js のページにFirebase認証を導入した際、環境変数にハマったメモ
- 投稿日:2020-04-21T20:32:58+09:00
React×GoでTodoリスト作ってみた
基本の復習も兼ねてTodoリストを作りました。
Reactでは、非同期でHTTP通信を行うためにaxiosを使っています。
Goでは、GORMとechoを使っています。React
Todo.jsimport React from 'react'; import InputField from './InputField'; import List from './List'; import http from './http'; class Todo extends React.Component { constructor() { super(); this.state = { text: "", lists: [], } } componentDidMount() { this.getTodoList(); } getTodoList = () => { return http .get('/todo') .then((response) => { this.setState({ lists: response.data }) }) .catch(error => { console.log(error) }) } handleChange = e => { this.setState({ text: e.target.value }) } handleSubmit = () => { if (this.state.text === "") { return window.alert("入力してください") } return http .post('/todo', { text: this.state.text }) .then(() => { this.setState({ text: "" }); this.getTodoList(); } ) .catch(error => { console.log(error) }) } handleDelete = (list) => { return http .delete(`/todo/${list.id}`) .then(() => this.getTodoList() ) .catch(error => { console.log(error) }) } render() { return ( <div className="todo"> <div className="todo-title"> <h1>Todo</h1> </div> <InputField text={this.state.text} handleChange={this.handleChange} handleSubmit={this.handleSubmit} /> <List lists={this.state.lists} handleDelete={this.handleDelete} /> </div> ) } } export default Todo;InputField.jsimport React from 'react'; export default function InputField(props) { return ( <div className="todo-input-field"> <input placeholder="入力しよう" value={props.text} onChange={e => props.handleChange(e)}></input> <button onClick={props.handleSubmit}>保存</button> </div> ) }http.jsimport axios from 'axios'; const API_HOST = process.env.REACT_APP_API_HOST || 'http://localhost:〇〇〇〇'; const http = axios.create({ baseURL: API_HOST, }); export default http;Go
main.gopackage main import ( "context" "fmt" "log" "net/http" "os" "os/signal" "パスに合わせて他のパッケージをimport" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) const defaultPort = "〇〇〇〇" func port() string { p := os.Getenv("PORT") if p != "" { return ":" + p } return ":" + defaultPort } func main() { connStr := fmt.Sprintf( "%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True", "DBUSER", "DBPASSWORD", "DBPROTOCOL", "DBNAME", ) db, err := gorm.Open("mysql", connStr) if err != nil { log.Fatal(err) } h := handler.New(db) server := &http.Server{ Addr: port(), Handler: h, } go func() { stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt) <-stop log.Println("Shutting down...") if err := server.Shutdown(context.Background()); err != nil { log.Println("Unable to shutdown:", err) } log.Println("Server stopped") }() log.Println("Listening on http://localhost" + port()) if err := server.ListenAndServe(); err != http.ErrServerClosed { log.Fatal(err) } }handler.gopackage handler import ( "net/http" "github.com/jinzhu/gorm" "github.com/labstack/echo" "github.com/labstack/echo/middleware" ) func New(db *gorm.DB) http.Handler { e := echo.New() e.Use(middleware.CORS()) h := &handler{ DB: db, } e.GET("/health", h.health) e.GET("/todo", h.getTodoLists) e.POST("/todo", h.createTodo) e.DELETE("/todo/:id", h.deleteTodo) return e } type handler struct { DB *gorm.DB } func (h *handler) health(c echo.Context) error { return c.JSON(http.StatusOK, map[string]string{"message": "OK"}) }list.gopackage handler import ( "net/http" "time" "github.com/labstack/echo" ) type List struct { ID uint `json:"id"` Text string `json:"text"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } func (h *handler) getTodoLists(c echo.Context) error { var lists []List err := h.DB.Find(&lists).Error if err != nil { return err } return c.JSON(http.StatusOK, lists) } func (h *handler) createTodo(c echo.Context) error { var list List err := c.Bind(&list) if err != nil { return err } err = h.DB.Create(&list).Error if err != nil { return err } return c.JSON(http.StatusOK, list) } func (h *handler) deleteTodo(c echo.Context) error { var list List paramID := c.Param("id") err := h.DB.Where("id=?", paramID).Delete(&list).Error if err != nil { return err } return c.JSON(http.StatusOK, list) }本来であれば、type ~ struct 構造体を他のパッケージからインポートするのがベストな気がするのですが、importがうまくできなかったので、list.goにまとめて書きました。
この問題は解決したい、、、DB
todoというデータベースの中にlistsテーブルがあり、中身は画像のようになってます。
Todoリスト
このような形で簡単なTodoリストが作成できると思います。
- 投稿日:2020-04-21T13:38:37+09:00
Gatsby+microCMSで手を打つ
Gatsby+microCMSで手を打つ
Intro
Angular…、そしてNext.js…。
SPAを開発しているのにSSRを考えないとイカン…なことが多くAngularから流れてくる人多いと思う。そんなこともあってNext.jsを始めたがページリロードすると404エラーとか…、それを解決するのにFunctionをとか導入して関数を書いてサーバー側で…とかもういいかげんにしてと思ってきた。
Nuxtにするか、とまた本買ってしまったが調べてみるとこれでもどうも同じことになるようで無駄な出費またまたあーもーとかなってるところにGatsbyって来て、情報が少ないものの「URLを直叩きしようがリロードしようが、404エラーが帰ってくることはなくなります」(Reactの最強フレームワークGatsby.jsの良さを伝えたい!!)
ということなので賭けてみる。Gatsbyプロジェクト作成
- node インストール
- git インストール
- gatsby-cli インストール
npm install --global gatsby-cli
- プロジェクト作成
gatsby new <プロジェクト名>
(gatsby-default-starterがあたる)- プロジェクトフォルダに移動してローカル環境立ち上げ
gatsby develop
- http://localhost:8000 にアクセス
![]()
フォルダ構造をみてみる。
とってもNextと似ているのでつい最近の苦労は無駄でなかったと救われる。
gatsby-config.js
というファイルがプラグイン管理
その他のgatsby-ほにゃらら.jsファイルはデフォルトでカラ。package.jsonを見てみる
{ "name": "gatsby-starter-default", "private": true, "description": "A simple starter to get up and developing quickly with Gatsby", "version": "0.1.0", "author": "Kyle Mathews <mathews.kyle@gmail.com>", "dependencies": { "gatsby": "^2.20.12", "gatsby-image": "^2.3.1", "gatsby-plugin-manifest": "^2.3.3", "gatsby-plugin-offline": "^3.1.2", "gatsby-plugin-react-helmet": "^3.2.2", "gatsby-plugin-sharp": "^2.5.3", "gatsby-source-filesystem": "^2.2.2", "gatsby-transformer-sharp": "^2.4.3", "prop-types": "^15.7.2", "react": "^16.12.0", "react-dom": "^16.12.0", "react-helmet": "^6.0.0" }, "devDependencies": { "prettier": "2.0.4" }, ・・・以下略・・・reactから派生したんだなとわかる。
microCMSとの連携
ヘッドレスCMSはmicroCMSが評判が良いようなのでこれで。
- microCMS用のプラグインをインストールする。
$ yarn add gatsby-source-microcms
- つぎに
gatsby-config.js
を編集する・・・前略・・・ `gatsby-plugin-sharp`, { resolve: `gatsby-plugin-manifest`, options: { name: `gatsby-starter-default`, short_name: `starter`, start_url: `/`, background_color: `#663399`, theme_color: `#663399`, display: `minimal-ui`, icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site. }, }, // 追加↓ { resolve: "gatsby-source-microcms", options: { apiKey: "X-API-KEY", serviceId: "guitar-club", endpoint: "news", }, }, // 追加↑ ], }micorCMSのAPI-KEY, サービスID、エンドポイントをoptions:{...}に書けばよいようである。書かないで
gatsby develop
してもエラーでたち上がらない。なのでmicroCMSの設定を。
microCMS準備
microCMSのサイトはこちら→ https://microcms.io/
ここでアカウント作成してログイン。
ここでは医療系情報サイトをイメージして、一般情報、患者さん向け、医師向け、といったカテゴリーがあり、それぞれのカテゴリーに多くの情報ページがぶらさがる単純な2階層のWebサイトをイメージして作ってみる。まずはサービス名とサービスIDを入力。サービスIDはエンドポイントのサブドメインになる(※ https://<サービス名>.microcms.io/ となる)。自分のプロジェクト名とかサイト名がいいだろうわかりやすいし。たとえばhogehoge.jpというサイトだったらhogehogeとか。
次にコンテンツを作成する。まずはカテゴリーというAPIを作る。左端メニューの「コンテンツ ✙」の✙をクリック。
患者向けカテゴリーとしてpatientsと入力した。公開ボタンをクリック
ほかのカテゴリーも同様に追加していく。ここでは医師向けとしてdoctors、一般情報としてbasicsと。
次にカテゴリーの下にぶらさげるコンテンツを作っていく。
またコンテンツ✙をクリックして、記事コンテンツ、articlesと入力
種類とあるが、だいたい良く知ったようなものだ。テキストフィールドとかテキストエリアとか。ブログみたくリッチに編集したかったらリッチエディタで画像やリンクを張ったりもできる。カラーピッカーはまだないようだが・・・
最後にカテゴリーフィールドをつくる。種類は「複数コンテンツ参照」というのを選び、さっき作った「カテゴリー」APIを選んで決定。
GET /articles/{CONTENT_ID}とあるが、つまりエンドポイントは
https://<サービスID>.microcms.io/api/v1/articles/
になる。gatsbyの場合、gatsby-config.js
のendpoint:に"articles"とだけ書けばいいが、Next.jsなどではgetInitialProps()でconst res = await axios.get(`https://<サービスID>.microcms.io/api/v1/articles/`, key )のようにしなければならないようである。参考:Next.js + microCMS + NetlifyでJAMstackな世界に入門する
APIキーもここに載ってる。表示ボタンをクリックして表示させてコピーして使う。
次にAPI設定をクリック。
ここでさっき作ったフィールドを削除、追加、編集できる。なので最初は適当にタイトルとか内容(コンテンツ)とかとかまあ適当に・・・
あとはブログみたく中身をいれるだけ。入力したら公開をクリック。
「カテゴリー」フィールドは患者さん向けのpatientsにした(入力するときpatients, doctors, basicsから選ぶ)。「紹介アイコン」フィールドは種類を「画像」にしたpictというフィールドである。
このようにして、カテゴリーpatientsの記事を2つ作ってみた。
患者さん向け記事①
患者さん向け記事②
ではコーディング
各記事紹介ページを作成
まずは
gatsby-config.js
にAPI、サービスID、エンドポイントを設定。
pagesの下にpatients.jsを作成、以下のようにコード。
// pages/patient.js import React from "react" import { graphql } from "gatsby" import Layout from "../components/layout" import SEO from "../components/seo" const PatientsPage = ({ data }) => ( <Layout> <SEO title="患者の方へ" /> {data.allMicrocmsArticles.edges.map(edge => { const articles = edge.node const category = edge.node.category[0].name console.log('◆categoryは ' + category) if (category == 'patients') { //カテゴリーが患者さん用の場合表示 return ( <React.Fragment key={articles.id}> <div> <h2>{articles.title}</h2> <p>{articles.feature}</p> <img src={articles.pict.url} width={110} height={110} alt="pict画像" /> </div> <div> {articles.category.map(category => ( <React.Fragment key={category.id}> <span>カテゴリー:{category.name}</span> </React.Fragment> ))} </div> <hr /> </React.Fragment> ) } else { return } })} </Layout> ) export const query = graphql` { allMicrocmsArticles( sort: { fields: [createdAt], order: DESC } ) { edges { node { id title title_origin category { id name } pict { url } body feature } } } } ` export default PatientsPageそして
gatsby develop
で立ち上げてhttps://localhost:8000/patients
にアクセスする。ふたつの記事のタイトルと紹介文と紹介画像が降順で表示されている。
GraphQLというのはよくわからんがこんな書き方をするのだなあ、と。
// pages/patients.js ・・・略・・・ export const query = graphql` { allMicrocmsArticles( sort: { fields: [createdAt], order: DESC } ) { edges { node { id title title_origin category { id name } pict { url } body feature } } } } ` ・・・略・・・
http://localhost:8000/___graphql
にアクセスしてみる。するとGraphQLで受け取ってるデータとデータ構造が見れる。
allMicrocmsArticlesのedgesのnodeに記事のすべてが詰まってる。
GraphQLでうけとったデータをmapで回してじゅんぐりに表示、ということである。
個別記事の全表示ページを作成
動的なページを作成する際にはデフォではカラだったgatsby-node.jsファイルにコードする(えっ?)。そういうキマリ。
// gatsby-node.js const path = require("path") exports.createPages = async ({ graphql, actions }) => { const { createPage } = actions const result = await graphql( ` { allMicrocmsArticles { edges { node { id title title_origin category { id name } body feature pict { url } } } } } ` ) if (result.errors) { throw result.errors } result.data.allMicrocmsArticles.edges.forEach(edge => { //上記のGraphQLでcategoryを書いてないがnode.categoryを掴めるようだ const categoryName = edge.node.category[0].name switch (categoryName) { case 'patients': // categoryがpatientsだったらサブパスをpatientsに subDir = '/patients/'+ edge.node.id break; case 'doctors': // categoryがdoctorsだったらサブパスをdoctorsに subDir = '/doctors/'+edge.node.id break; default: subDir = '/articles/'+edge.node.id } createPage({ //path: `/patients/${edge.node.id}`, path: `${subDir}`, component: path.resolve( "./src/templates/article.js" ), context: { id: edge.node.id, }, }) }) }動的なページを作成するためにGatsbyにはcreatePagesというAPIが用意されてて、上のコードで肝になるのはそのcreatePages関数の中だけ。わたしは患者向け記事と医師向け記事と…でサブパスを分けたかったのでSwitch文があるが、単純に以下のようにシンプルでよい。
result.data.allMicrocmsArticles.edges.forEach(edge => { createPage({ path: `/articles/${edge.node.id}`, component: path.resolve( "./src/templates/article.js" ), context: { id: edge.node.id, }, }) })
component: path.resolve(
"./src/templates/article.js"
)
ではテンプレートビューファイルであるarticle.jsを指定してる。次のステップで作成する。
作成先がtemplatesフォルダになるのはそういうキマリかもしくは慣習。
context: {
id: edge.node.id,
}
では記事データのIDを指定している。こう書くだけで次のステップで作成するテンプレートに値渡しできるようである。ではtemplatesフォルダを作成。
そしてその下にarticle.jsファイルを作成(ファイル名は任意)。//templates/article.js import React from "react" import { graphql } from "gatsby" import Layout from "../components/layout" const ArticlePost = props => { const post = props.data.microcmsArticles // ㊟allMicrocmsArticleでない return ( <Layout> <div> <h2>{post.title}</h2> <h3>原文:{post.title_origin}</h3> <br /> <img src={post.pict.url} width={160} height={110} alt="pict画像" /> <p dangerouslySetInnerHTML={{ __html: `${post.body}`, }} ></p> </div> </Layout> ) } export default ArticlePost export const query = graphql` query($id: String!) { microcmsArticles(id: { eq: $id }) { title title_origin body pict { url } body } } `34行目の
microcmsArticles(id: { eq: $id })
で、gatsby-node.jsのcontextで指定した記事IDが値渡しされている。そしてそのIDをもとにmicrocmsArticlesからデータをGraphQLで引っ張っている。個別の記事をとってきたいのでallMicrocmsArticleではないところ、注意。
さいごにpatients.jsにリンクをはる。// pages/patients.js ・・・略・・・ <React.Fragment key={articles.id}> <div> <Link to={`/patients/${articles.id}`}> <h2>{articles.title}</h2> </Link> <p>{articles.feature}</p> ・・・略・・・これでgatsby developでビルドしなおすと(gatsby-node.jsファイルのコードを変更すると、変更内容にもよるがビルドしなおさないといけないみたい)詳細ページが表示されるようになる。
URLはコレ
http://localhost:8000/patients/a9cd9e19-8a1f-5ad9-b753-00bc8dbfa875
titleをURLにしたかったらedge.node.titleにすれば以下のようになるだろう。日本語がイヤだったら記事コンテンツAPIに英語タイトルのフィールドを追加するだけだ(半角スペースはいれないよう)
http://localhost:8000/patients/患者さん用記事②リロードしても404エラーにならない。すばらしい。
補足
pagesフォルダの下にdoctors.jsファイルを作成、patients.jsのコードをコピペして少し編集しただけ。microCMS側では記事を作成時、カテゴリーをdoctorsにするだけだ。
もちろん、
gatsby-node.js
とtemplates/article.js
はなにも変更する必要ない。// pages/doctors.js import React from "react" import { graphql, Link } from "gatsby" import Layout from "../components/layout" import SEO from "../components/seo" const PatientsPage = ({ data }) => ( <Layout> <SEO title="医師の方へ" /> {data.allMicrocmsArticles.edges.map(edge => { const articles = edge.node const category = edge.node.category[0].name console.log('◆categoryは ' + category) console.log('◆articles.idは ' + articles.id) console.log('◆リンク先は ' + `/doctors/${articles.id}`) if (category === 'doctors') { //カテゴリーが医師用の場合表示 return ( <React.Fragment key={articles.id}> <div> <Link to={`/doctors/${articles.id}`}> <h2>{articles.title}</h2> </Link> <p>{articles.feature}</p> <img src={articles.pict.url} width={110} height={110} alt="pict画像" /> </div> <div> {articles.category.map(category => ( <React.Fragment key={category.id}> <span>カテゴリー:{category.name}</span> </React.Fragment> ))} </div> <hr /> </React.Fragment> ) } })} </Layout> ) export const query = graphql` { allMicrocmsArticles( sort: { fields: [createdAt], order: DESC } ) { edges { node { id title title_origin category { id name } pict { url } body feature } } } } ` export default PatientsPage
参考:
gatsby-source-microcms
話題のHeadlessCMS「microCMS」を試してみたwith Gatsby ~part2 Gatsbyを使ってViewを表示~
Next.js + microCMS + NetlifyでJAMstackな世界に入門する
- 投稿日:2020-04-21T09:09:22+09:00
新卒エンジニアに送るReact + Reduxの習熟度チェックリスト
なんとなくだけどこれくらいできるといいなーと思ったことをまとめていたらチェックリスト方式になったので公開。まだ項目として足りない部分があるけどそれは今後追加していけば良いかなと。
これ足りないよ〜とかありましたらコメントいただけますと嬉しいです。
前提
- Create React Appでプロジェクトを開始できる
- React公式のチュートリアルを完了している
初級者
- Stateの更新・propsの使用ができる
- 配列を使って要素の描画ができる
- formを使ってpostすることができる
- ライフサイクルメソッドを使うことができる
- UIライブラリを使ってスタイルを付けられる
簡単なビューであれば作成・修正ができるレベル
- lintの設定ができる
- Formaterの設定ができる
- react-routerを使いページ遷移をさせることができる。
- 配列内のkeyの意味がわかる。また、keyにどんな値を入れるのが適切なのか述べることができる
- 余計なdivタグを書かずにマークアップできる(fragmentを使う)
- スタイルの適用ができる
基本的なビューであれば問題なく任せられるレベル
- classコンポーネントと関数コンポーネントの違い・メリット・デメリットがわかる
- TypeScriptを使って書くことができる
- TypeScriptで書くことのメリットを述べることができる
- Reduxが何をしているか理解している
- Reduxを使ってStateの更新をすることができる
- APIを使ってデータの取得・更新ができる。また、Storeに入れることができる。
- hooksを使ってコードを書くことができる
- ライフサイクルメソッドを理解して使うことができる。また、あまり使用しない方が良い理由を述べることができる
1人月として計算ができレビューも任せられるレベル
- テストコードを書くことができる
- メモリーリークに気をつけてコーディングできる
- ログイン・サインアップなどAuth周りの実装ができる
- Reduxのメリット・デメリットを述べることができる
- middlewareで処理をすることができる。また、そのメリットデメリットを述べられる
- ReactのComponentとは何か理解している
- 必要であればカスタムhooksを作ることができる
- Code-Splittingを使うことができる。また、そのメリットを述べることができる。
- レビューすることができる
0からのプロジェクトでも難なくこなせるレベル
- Componentの設計ができる
- エラーハンドリングを実装できる
- Reduxを使う基盤を設計することができる
- パフォーマンスの改善ができる
- そのプロジェクトでどの範囲のテストをする必要があるのか判断でき、指標を示すことができる