- 投稿日:2020-07-26T21:44:15+09:00
Next.jsで、同じ変数なのに属性内とテキスト内とで違う値になる
環境
- Next.js 9.4.4
- React 16.13.1
再現手順
Next.jsで、下記のようなコンポーネントを定義します。そして
next dev
で開発サーバを立ち上げるか、next build && next export
で静的サイト生成して、サイトを表示します。pages/index.jsexport default function Home() { const now = Number(new Date()); return ( <> <p><a href={now}>{now}</a></p> </> ) }このときの
now
変数で表示される値が、href属性とaタグ内部テキストとで同じ変数なのに異なる値を取ってしまいます。ふたつの値は、それぞれ下記のものになっています。
- href属性内の値は、サーバサイドでのページ生成時の値(静的サイトのビルド時の値)
- テキストは、クライアントサイドでのページ表示時の値
開発サーバ立ち上げ時はブラウザのコンソールに
prop did not match. Server: ..., Client: ...
という警告が出るので気づきやすいですが、静的サイト生成時だと表示されません。逆に、静的サイト生成の方で試すとエラーは出ませんが違いが大きいため分かりやすいです。適当にステートを更新して明示的に再描画すると、それ以降ふたつの値はちゃんと一致するようになります。
pages/index.jsexport default function Home() { const now = Number(new Date()); const [_, setHoge] = React.useState(); return ( <> <p><a href={now}>{now}</a></p> <input type="button" value="refresh" onClick={()=>{setHoge(Math.random())}} /> </> ) }また、Productionモードで動作させたときもこのような現象は発生しないようです。
原因
どうやらNext.jsのAutomatic Static Optimizationという機能が原因のようです。サーバサイドとクライアントサイド両方で生成が可能な変数で、かつサーバとクライアントとで値が変わってしまう場合に、このような現象が発生するようです。
この問題は、getStaticPropsを使うと値が一致するようになります。
pages/index.jsexport default function Home({ now2 }) { return ( <> <p><a href={now2}>{now2}</a></p> </> ) } export async function getStaticProps() { const now2 = Number(new Date()); return { props: { now2 } } }またはAutomatic Static OptimizationはgetInitialPropsという機能を使うことで無効になります。
pages/index.jsexport default function Home({ now2 }) { return ( <> <p><a href={now2}>{now2}</a></p> </> ) } Home.getInitialProps = async (_) => { return { now2: Number(new Date()) }; }ただしこれらの方法を使うと、静的サイトのビルド時に(サーバサイドで)生成された値が更新される事はありません。静的サイト生成を使う場合は特に困りますね。
もし静的サイト生成を使わない場合(サーバサイドレンダリングする場合)は、getServerSidePropsを使えば良いようです。
pages/index.jsexport default function Home({ now2 }) { return ( <> <p><a href={now2}>{now2}</a></p> </> ) } export async function getServerSideProps() { const now2 = Number(new Date()); return { props: { now2 } } }解決方法
componentDidMount
に引っ掛けることで、サーバサイドで描画されないように(クライアントサイドで一度レンダリングが走った後に、実際のDOMが描画されるように)します。useEffect()
を使う場合は下記のような感じです。pages/index.jsexport default function Home() { const now = Number(new Date()); const [isMounted, setIsMounted] = React.useState(false); React.useEffect(() => setIsMounted(true)); if (!isMounted) return <>Loading...</>; return ( <> <p><a href={now}>{now}</a></p> </> ) }参考
- 投稿日:2020-07-26T17:07:37+09:00
【Mac】「Docker」で「Ruby on Rails 6」「React」と「MySQL 8」で環境構築(CRUDのサンプル付)
■ はじめに
SESエンジニアとして、PHPをメインに参画し、現在はJava案件に参画しているTatsuyaです。
そしてこの度転職が決まりまして、9月から新しい職場に!!!!
楽しみもある反面、実は不安要素が。。。それは、、、
次の職場は!!Ruby on Rails!!やったことない!!僕「学習しないと転職した瞬間からお荷物確定ダァー」
と焦り散らかし始めて、先日からRuby on Railsの学習に励み始めたわけなのですが。。。
僕「Rubyの公式チュートリアルめちゃくちゃ大変すぎないか!?さらにHerokuとかも使うの!?結構大掛かり!?」
というので、実は挫折しちゃいました。。。
挫折理由としては、チュートリアル自体が骨太ということもありますが、 Dockerを使った環境構築 も重なって折れちゃいました。(最弱)僕は、直接自分のMacにRuby環境は作りたくなかったんで、なんとしてでもDockerでローカル環境を整えたかったのです。
(次の職場でもDocker使うみたいですしお寿司)ということで、先人の方々が丁寧に書き上げていただいた貴重な文献を元に、実際に動かしながらRubyの動きがわかるような良い感じの環境を作らせていただいて、そこで学習を進めようという方向に舵を切ることになりました。
その過程で、とてもわかりやすく、実際に手を動かしながら理解しやすいと感じた「環境」「サンプルコード」を有り難くミックスさせていただいたので、そちらの結果の方をこの記事でご紹介できればなと思います。
◇ 大変お世話になった貴重な参考文献(無許可です。すいません。)
以下に記載した方々のお力をお借りして、良い感じのサンプルサイトの構築ができました。ありがとうございます。
これから環境構築作業に入っていきますが、丁寧に進めたい!という方はぜひ僕の記事と一緒に偉大なる先人方の記事を見ていただければかなり深まるのではないかと思います。◉ 環境
Docker + Ruby on Rails + MySQLで開発環境を新規構築する
- 丁寧な記事で、詳しくソース毎の解説をしてくれています。またDockerのインストールから記載していただいているので、初めてDockerに触るという方にもオススメです。とてもありがたかったです。
◉ サンプルコード
Rails + React + AjaxでCRUDのサンプルプロジェクト
Rails + React + AjaxでCRUDのサンプルプロジェクト(ソース一式)
- チュートリアル方式で解説を進めてくださっております。大変わかりやすいです。私はまるっとクローンして動かしたのですが、書きながら学びたい方は順に進めていくのも良いと思います。
◉ その他細かい部分でお世話になった記事
Rails on Dockerでcredentialsをeditしたい
◇ 感謝と謝罪
- ※基本的に参考記事様の情報をコピペで使用しております。私が今回取り組んだのは、 環境とサンプルコードのミックス なので、変にアレンジさせずにほぼ原型のままで使用させていただいております。貴重なお時間を割いて書き上げていただいた記事を丸々コピーするような形になりまして、大変申し訳ないという気持ちと、Rubyの学習コストを下げていただいて誠に有り難うございますという感謝と謝罪を先にさせていただきたいと思います。
■ 実際に環境を構築していきましょう
◇ 前提条件
- Mac
- Docker インストール済みであること
- まだの方はこちらの記事から
- git インストール済みであること
◇ 最終的なディレクトリ構成
ディレクトリ構成mpp_react_crud /mysql /Dockerfile /my.cnf /rails /app /bin /config /db /log /node_modules /public /tmp .browserslistrc .gitignore .ruby-version .babel.config.js .config.ru Dockerfile Gemfile Gemfile.lock LICENSE package.json pastcss.config.js Rakefile README.md yarn.lock docker-compose.yml◇ サンプルプロジェクトをクローン
terminal# homeへ $ cd # mpp_react_crud ディレクトリを作成 $ mkdir mpp_react_crud # mpp_react_crud ディレクトリに移動 $ cd mpp_react_crud # サンプルプロジェクトをclone $ git clone https://github.com/TakeshiOkamoto/mpp_react_crud.git # mpp_react_crudプロジェクト の名前を railsに変更 $ mv mpp_react_crud rails◇ docker-compose.yml の作成
terminal# 『◇ サンプルプロジェクトをクローン』の続きから # docker-compose.yml を作成する $ vi docker-compose.yml # vim画面が開いたら、「iキー」でINSERTモードにし、以下のymlをコピペしてください。 # コピペが完了したタイミングで、「:wq」で保存をして終了してください。docker-compose.ymlversion: "3.7" services: db: build: mysql image: mpp_react_crud_db container_name: mpp_react_crud_db ports: - 3306:3306 app: build: rails command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" image: mpp_react_crud_app container_name: mpp_react_crud_app ports: - 3000:3000 volumes: - ./rails:/rails depends_on: - db◇ MySQL用のDockerfileとmy.cnfを作成
▼ Dockerfile の作成
terminal# 『◇ docker-compose.yml の作成』の続きから # mysql ディレクトリを作成 $ mkdir mysql # mysql ディレクトリに移動 $ cd mysql # Dockerfile を作成する $ vi Dockerfile # vim画面が開いたら、「iキー」でINSERTモードにし、以下のymlをコピペしてください。 # コピペが完了したタイミングで、「:wq」で保存をして終了してください。DockerfileFROM mysql:8.0.18 ENV MYSQL_ROOT_PASSWORD root_pass COPY ./my.cnf /etc/mysql/conf.d/my.cnf RUN mkdir /var/log/mysql RUN chown mysql:mysql /var/log/mysql RUN mkdir /var/run/mysql RUN chown mysql:mysql /var/run/mysql▼ my.cnf の作成
terminal# my.cnf を作成する $ vi my.cnf # vim画面が開いたら、「iキー」でINSERTモードにし、以下のymlをコピペしてください。 # コピペが完了したタイミングで、「:wq」で保存をして終了してください。my.cnf[mysql] default-character-set=utf8mb4 [mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_bin datadir=/var/lib/mysql socket=/var/run/mysql/mysql.sock log-error=/var/log/mysql/mysqld.log pid-file=/var/run/mysql/mysqld.pid port=3306 default_authentication_plugin= mysql_native_password [client] default-character-set=utf8mb4terminal# 最後に mpp_react_crud ディレクトリに戻る $ cd ..◇ Ruby on Rails用のDockerfileとGemfile.lockを作成
▼ Dockerfile の作成
terminal# 『◇ MySQL用のDockerfileとmy.cnfを作成』の続きから # rails ディレクトリに移動 $ cd rails # Dockerfile を作成する $ vi Dockerfile # vim画面が開いたら、「iキー」でINSERTモードにし、以下のymlをコピペしてください。 # コピペが完了したタイミングで、「:wq」で保存をして終了してください。DockerfileFROM ruby:2.6.5 ENV LANG C.UTF-8 ENV APP_HOME /rails RUN apt-get update -qq && apt-get install -y build-essential nodejs # もしyarnでエラーが発生した場合 RUN apt-get update -qq && apt-get install -y curl && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && apt-get update && apt-get install -y yarn && apt-get install -y vim RUN rm -rf /var/lib/apt/lists/* RUN mkdir $APP_HOME WORKDIR $APP_HOME COPY ./Gemfile $APP_HOME/Gemfile COPY ./Gemfile.lock $APP_HOME/Gemfile.lock RUN bundle install COPY . $APP_HOME EXPOSE 3000▼ Gemfile.lock の作成
terminal# Gemfile.lock を作成 # 空ファイルで良いので、touch コマンドで作成します。 $ touch Gemfile.lock◇ Rails用データベース設定ファイル"database.yml"を編集
terminal# 『◇ Ruby on Rails用のDockerfileとGemfile.lockを作成』の続きから # database.yml を編集 $ vi config/database.yml # vim画面が開いたら、「iキー」でINSERTモードにし、以下のymlをコピペしてください。 # コピペが完了したタイミングで、「:wq」で保存をして終了してください。database.ymldefault: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: root_pass host: db development: <<: *default database: mpp_react_crud_development test: <<: *default database: mpp_react_crud_test production: <<: *default database: username: password:terminal# 最後に mpp_react_crud ディレクトリに戻る $ cd ..◇ Dockerイメージをビルド
terminal# 『◇ Rails用データベース設定ファイル"database.yml"を編集』の続きから # Docker イメージをビルド $ docker-compose build◇ docker-composeでアプリを起動〜プロジェクト設定
terminal# 『◇ Dockerイメージをビルド』の続きから # サービスの立ち上げ $ docker-compose up -d # -d: バックグラウンドで起動 # bundle インストール $ docker-compose run app bundle install # 各種パッケージのインストール $ docker-compose run app yarn install # マスターキーの生成 # ファイル生成後、credentials.yml.encの編集画面が表示されるので:q!で終了します。 $ docker-compose run -e EDITOR=vim app rails credentials:edit # フォルダの生成 $ mkdir rails/app/assets/images◇ データベース準備
terminal# 『◇ docker-composeでアプリを起動〜プロジェクト設定』の続きから # データベースの作成 $ docker-compose run app rails db:create # 各テーブルの作成 $ docker-compose run app rails db:migrate # 各テーブルの初期データの作成 $ docker-compose run app rails db:seed◇ 完成!
最後にコンテナを再起動しましょう。
terminal$ docker-compose restart
再起動終了後に、http://localhost:3000 にアクセスしてみてください!
すると、以下のようなページが開かれます。以上でサンプルサイトの環境構築終了です!
■ 終わりに
初心者の方だけでなく、経験者の方でもあっても、初めて触る技術の学習にはかなり苦戦すると思います。
そのため、先に「どういうサイトがどのように動いているか」ということを知るだけでも学習効率は上がるんじゃないかなーと思い、今回の作業に取り掛かりました。
まだ私も環境構築をしてすぐにこの記事を書いているので、この環境を使った学習は始めていませんが、実際にログを仕込んでみたりしながら体感的に学ぶ方が飲み込みは早いと考えるので、ガンガンこの環境とこのサイトを元に学習に取り組んでいこうと思います。この記事は解説部分を完全に端折って、ただただ完成させることだけを目標に書き上げました!
そのため、しっかりと解説を確認したい方は、参考文献様の記事をご確認いただいて、照らし合わせながら進めてほしいと思います。僕「本当に先人の方々はすげえや。」
- 投稿日:2020-07-26T12:26:53+09:00
React Hooksって結局いつ使うの?
導入
最近、React Hooksについて勉強しているのですが、どんなことができるのかなんとなく分かってもどういうメリットがあるのかイマイチ分からないということがあります
そこで今回はReact Hooksを使うとどんなメリットがあるのかをいろいろと調べてみたので学習記録という意味でもまとめてみようと思います
まず最初に:useStateとレンダリングの仕組み
useStateはstateとそのstateに値を入れるsetStateの組を生成します
const [state, setState] = useState(null); setState('test'); // stateに'test'が入るなぜuseStateを使うかを理解するにはレンダリングの流れを理解する必要があります。
以下のコードを例に解説しますimport React, { useState } from 'react'; export const Page = () => { const [text, setText] = useState(null); if(!text) { setText('test'); console.log(text); } console.log(text); return ( <div className="Page"> {text} </div> ); }こちらのコードを実行すると画面にはtestとだけ表示されます
以下実行の流れを見ていきますconst [text, setText] = useState(null);まず最初のuseStateでtextとsetTextを定義します。このときtextにはnullが入っています
if(!text) { setText('test'); console.log(text); } console.log(text);textがnullならtextにtestを入れます。textは最初nullなのでここを通り、textには'test'が入ります。
ここで注意しなければならないのはsetTextはすぐに機能しないということです。setTextはtextの値を【次のレンダリングから】testに変更することを意味していますなのでこの時点ではtextの値は変わっておらずconsoleではどちらもnullが表示されますreturn ( <div className="Page"> {text} </div> );その状態で一度returnされます。したがって一回目のレンダリングでは何も表示されません。setTextによりtextが変更されたのでそのまますぐに2回目のレンダリングがtext=testとして実行されます
2回目のレンダリングではtextはnullではないのでsetTextは呼ばれず、そのままtext='test'が出力されます
useStateで実現できること
useStateで実現できるのは
画面内でのアクションによる変化を画面に反映させること
ですこちらも例を見てみます
export const Page = () => { let text = null if(!text) { text = 'test'; console.log(text); } console.log(text); return ( <div className="Page"> <button onClick={() => {text = 'clicked'}}>button</button> <div>{text}</div> </div> ); }ボタンをクリックすると表示される内容を変えるという内容ですが、これだと再レンダリングされないため、いくらボタンを押しても表示されるのはtestのまま変わりません
ボタンを押したことを画面に反映させるにはuseStateを使わなければなりません。export const Page = () => { const [text, setText] = useState(null); if(!text) { setText('test'); console.log(text); } console.log(text); return ( <div className="Page"> <button onClick={() => {setText('clicked')}}>button</button> <div>{text}</div> </div> ); }こちらのコードを使うとちゃんと表示されるtextが変化します。
なお、変更内容を画面に表示しなくてよければuseStateは必要ありません。
export const Page = () => { let text = null if(!text) { text = 'test'; console.log(text); } console.log(text); return ( <div className="Page"> <button onClick={() => {text = 'clicked'}}>button</button> <button onClick={() => {console.log(text)}}>display</button> <div>{text}</div> </div> ); }このコードで一度buttonを押してからdisplayを押すとconsoleにはpushedが出力されます
useEffectで実現できること
useEffectによって
一部に依存した処理の不要な呼び出しを防ぐこと
ができますuseEffectは
useEffect( (), //関数 [] //依存する変数 )という形で依存する変数が変化したときのみ関数処理を行うことができます
useEffectを使わないとこのようなコードになります
export const Page = () => { const [clicked, setClicked] = useState(false); const [text, setText] = useState(null); if(clicked && !text) { setText('test'); console.log(text); } console.log(text); return ( <div className="Page"> <button onClick={() => {setClicked(true)}}>button</button> <button onClick={() => {setText(null)}}>clear</button> <div>{text}</div> </div> ); }こちらのコードではbuttonを押すとtextにtestが入ります。
しかし、clearを押してtextをnullにしたときもif文の中に入ってしまい、そこでtextに値が入ってしまうためclearの方は上手く動きません。useEffectを用いるとこのように書けます
export const Page = () => { const [clicked, setClicked] = useState(false); const [text, setText] = useState(null); useEffect( () => { if(clicked) { setText('test'); console.log(text); } }, [clicked] ) console.log(text); return ( <div className="Page"> <button onClick={() => {setClicked(true)}}>button</button> <button onClick={() => {setText(null)}}>clear</button> <div>{text}</div> </div> ); }こちらではclearを押してもtextは変わっていないためuseEffectの中は呼ばれず、textの値も変わりません。
値の変化に連動してstateを変化させる処理はuseEffectを使わないと複雑になりがちなので複数の値を連動させて変化されるときはuseEffectを使うのが良いと思われますmemo, useCallbackでできること
memoは
一部に依存したコンポーネントの呼び出しを最小限にすること
useCallbackは
一部に依存したfunctionの呼び出しを最小限にすること
ができます役割が似ていることもあり、この2つは一緒に使われることが多いです
useCallbackは
const functionA = useCallback( (), //関数 [] //依存する変数 )という形で書くことができ、このようにするとfunctionAは依存する変数が変化しない限り再生成されず、同じ関数を使い回すことができます。
memoも同様で
const ComponentA = memo((props) => { // コンポーネントの内容 })と書くことができ、propsの内容が変わらない限り再レンダリングされず、同じコンポーネントを使い回すことができます。
以下のコードを例に考えてみます
(例はこちらを参考にさせていただきました)const Child = ({handleClick}) => { console.log('child render') return( <button onClick={handleClick}>child button</button> ) } export const Page = () => { const [count, setCount] = useState(0); console.log('render'); return ( <div className="Page"> <button onClick={() => {setCount(count + 1)}}>button</button> <Child handleClick={() => console.log('child')} /> <div>{count}</div> </div> ); }こちらのコードではbuttonをクリックするとsetCountが走り、それによってPageだけでなく、変化していないChildも再レンダリングされてしまいます。memoやuseCallbackを使うことでこのような不要な再レンダリングを減らすことができます
const Child = memo((props) => { console.log('child render') return( <button onClick={props.handleClick}>child button</button> ) }) export const Page = () => { const [count, setCount] = useState(0); const handleClick = useCallback( () => { console.log('child'); }, [] ) console.log('render'); return ( <div className="Page"> <button onClick={() => {setCount(count + 1)}}>button</button> <Child handleClick={handleClick} /> <div>{count}</div> </div> ); }このようにすることで、まずuseCallbackにより、handleClickの再生成を避けることができます。すると、memoにより、ChildはhandleClickが変化しない限り再レンダリングされないようになっているのでChildの再レンダリングも避けることができ、結果的に不要なレンダリングを減らすことができるようになります。
useCallbackはmemoと一緒に扱われることが多いので勘違いしやすいですが、効果はあくまでもfunctionの再生成を防ぐことにあるのでmemoと併用する以外にも使用される場面はあると思われます(うまい例が浮かびませんが...)
関数の再生成を防ぐならuseCallback、コンポーネントの再レンダリングを防ぐならmemoと覚えておくといいですまとめ
useStateは画面内で発生した変化を画面に反映させるとき
useEffectは一部に依存した処理の不要な呼び出しを防ぐとき
useCallbackは一部に依存したfunctionの再生成を防ぐとき
memoは一部に依存したコンポーネントの再レンダリングを防ぐときに効果がある
参考
https://qiita.com/ossan-engineer/items/740425a0df937a47e093
https://qiita.com/uehaj/items/99f7cd014e2c0fa1fc4e
https://qiita.com/soarflat/items/b9d3d17b8ab1f5dbfed2
- 投稿日:2020-07-26T11:41:59+09:00
Laravel + React環境にESlintとPrettierを導入する
始めに
以下はLaravel + Reactの構成で、
Eslint
とPrettier
を導入する流れをメモした記事となります。bladeを使わずに、ReactやVueを利用するプロジェクトも増えてきてると聞いて、では、ESlintやPrettierを導入する時の手順はどうするんだろう?という事で、ちょっと試してみました。
Laravelのバージョンは、
5.7
です。
Laravel + Reactの構成で、Linterを導入したい人は、多少参考になるのではないかと思います。そもそもESlintって?
ESlintとは、JavaScriptのための静的解析ツールで、シングルクォートやスペースの記法のルールを統一したり、単純な構文エラーを検出してくれたりします。
ルールは細かく設定できるので、各プロジェクトによって固有のコーディング規約を定義することもできるので便利です。
詳しくは公式を...
ESlintそもそもPrettierって?
こちらは、Node.js上で動作するフォーマッターです。
インデントや、改行コードなどの基本的な設定をして、定義したフォーマットに従って自動的に整形してくれます。
Linterと役割が、重複する部分もありますが、Linterだけで整形できない部分もカバーしてくれたりします。(1行の長い文字列を整形する設定ができるなど)コードのフォーマットに関しては、Prettierで行い、構文チェックはESlintで行うという形で併用するのが良いかと思います。
こちらも詳しくは公式を...
Prettierなんで導入するの?
上記のようなLinterとFormatterを導入してない状態で、複数人開発をしている方は経験あるかもしれませんが、さまざまな書き方が混在し、1ファイルの中でも、複数の記法が混在し、統一性のない読みづらいコードとなってしまうことがあります。
あれ? シングルクォート、ダブルクォートが混在してる...
おぉ...パスカルケース、キャメルケースが混在してる...何か意図があるのかなぁ...?
ん? セミコロンつけるの?つけないの?...などこのような状態で開発が続いてしまうと、可読性が低く、メンテナンス性の悪いコードになってしまいます。また、プルリク時に、コーディング規約の議論に時間を奪われてしまうことも度々発生します。
フォーマットや命名規則は、プロジェクトに関わる開発者間で統一し、コードリーディング/レビュー時の可読性をあげることは、開発のスピード感をあげる上で重要です。それによって、開発者はコードスタイルに気をとらわれることなく、本質的なコーディングにフォーカスすることができます。
ESlintやPrettierのような優秀なフォーマッターが用意されてるので、それに頼って、可読性の高い、後から入ってきたメンバーに対しても優しいコードにしましょう。ツールに任せられることは優秀なツールに任せちゃいましょう。
Laravel+ReactのプロジェクトにESlintとPrettierを導入する
では、早速導入してみましょう。
まずはnpmコマンドでパッケージをインストール。
各環境で必要に応じて、パッケージをインストールして下さい。パッケージをインストールnpm install eslint eslint-loader eslint-plugin-react eslint-plugin-prettier eslint-config-prettier --save-dev
上記インストールが完了したら、
.eslintrc.json
を作成します。
これはESlintの設定ファイルです。.eslintrc.jsonを作成$ cd LaravelProject $ touch .eslintrc.jsoneslintrc.json{ "env": { "es6": true, "commonjs": true, "browser": true // Globalオブジェクト「window」「document」を許可 }, "extends": [ "eslint:recommended", "plugin:react/recommended", "plugin:prettier/recommended" ], "settings": { "react": { "pragma": "React", "version": "detect" } }, "parser": "babel-eslint", "parserOptions": { "sourceType": "module" }, "rules": { "prettier/prettier": [ "error", { "printWidth": 120, "tabWidth": 2, "trailingComma": "all", "bracketSpacing": true, "singleQuote": true, "semi": false } ] } }LaravelでESlintを使用するよう設定を変更する
Webpackが定義したルールをを元に、ファイル変更を監視してくれるように、
webpack.mix.js
を編集します。以下を参考にしています。
Laravel Mix Eslint Configwebpack.mix.jsconst mix = require('laravel-mix'); /* |-------------------------------------------------------------------------- | Mix Asset Management |-------------------------------------------------------------------------- | | Mix provides a clean, fluent API for defining some Webpack build steps | for your Laravel application. By default, we are compiling the Sass | file for the application as well as bundling up all the JS files. | */ //本番環境ではESLintは使用しません if (!mix.inProduction()) { mix.webpackConfig({ module: { rules: [ { enforce: 'pre', exclude: /node_modules/, loader: 'eslint-loader', test: /\.(js|jsx)?$/, options: { fix: true, cache: false, } } ] } }) }これでセットアップは完了です。
実際に試してみる
以下のような
セミコロン
のあるファイルがあります。
しかし、このファイルは、セミコロン
を許可しないルールが定義されてる場合、コーディング規約に則っていないコーディングをしているということになります。App.jsimport React from 'react'; import ReactDOM from 'react-dom'; import BooksApp from './BooksApp'; const App = () => { return ( <div> <BooksApp /> </div> ); }; export default App; if (document.getElementById('app')) { ReactDOM.render(<App />, document.getElementById('app')); }以下のコマンドを打ってみます。
eslint resources/js/components/App.js
すると、以下のようにコンソールにエラーが表示されます。
見るとセミコロンを削除して下さいと言われてますね。
この場合、いちいち手動で直すの面倒ですよね?その場合、以下コマンドを入力して下さい。
これによって、Eslint
とPrettier
が同時に実行されます。
(= 構文チェックとコードの整形が同時にできる)
これは、先ほどインストールしたeslint-config-prettier
によって実現できることです。eslint resources/js/components/App.js --fix
すると、リアルタイムに、上記
App.js
にあった余計なセミコロンが削除されます。
もう一度、以下コマンドを入力してみましょう。eslint resources/js/components/App.js
すると何もエラーが返ってこない状態になっています。
ここは個々のプロジェクトによって状況が異なるのでご自身の環境に読み替えてみて下さい。ワイルドカードも使えます。
eslint resources/js/components/*.js eslint resources/js/components/*.js --fix
最後に
以上で、
*.js
や*.jsx
ファイルに対して、ESlintとPrettierを導入することができました。
上記手順で、プロジェクトのコーディング規約を定義すれば、開発者間のコーディングスタイルの差異を統一することができるようになります。実際のプロジェクトでは、Gitフックなど使って、コミット時に、ESlintとPrettierを実行するという設定もするかなと思います。
- 投稿日:2020-07-26T11:21:12+09:00
React SpectrumでBuild a movie search app using React hooksを作ってみた
React Spectrumがすごい
React Spectrumがすごそうということで、実際に使って開発してみました。React Spectrum自体の設計のクオリティの高さは、Adobe製デザインシステム「React Spectrum」がすごいので紹介したいの記事が詳しいです。内部の作りはさておき、開発者視点で良かったと感じた点は以下です。
- Adobeが開発しているので信頼度が高い
- Componentのプロパティの統一感があり、わかりやすく使いやすい
- Flex/Gridが簡単にかける
- 基本的にタグで書いていく思想?でCSSを書かなくても大体いける。Flex/Gridもタグがある。
- classNameのプロパティは「UNSAFE_className」になっており、説明にも「最後の手段」と。
Build a movie search app using React hooksについて
2020年のフロントエンドマスターになりたければこの9プロジェクトを作れで1つめに紹介されていたプロジェクトです。元のサイトはこちら。フロントエンドマスターを目指した方なら作ってみたことがあるはず!React Spectrumを試すちょうどよい題材として活用させてもらいました。
React Spectrumを使ってカスタマイズ
完成したイメージ
こんな感じです。
(デザインがダサいと感じたら、それはReact Spectrumではなく私のセンスの問題です。)
スマホにするとこんな感じです。
文字サイズとかいい感じに変わってくれるところとか素晴らしいです。
ダークモードにするとこんな感じです。
本来はOSの設定に応じて切り替わりますが、Reactの勉強がてら切り替えボタンをつけてみました。文字色もうまく変えてくれています。
キャプチャではわかりませんが、検索APIを呼び出している間は、ProgressCircleがくるくる回ります。
中身の説明
リセットCSS
リセットCSSがなくても大丈夫かもしれませんが、hタグを使った時の余白が気に入らなかったので、リセットCSSを入れました。使用したのはmodern-css-resetです。はじめは、destyle.cssを使ってみたのですが、hタグで見た目の違いが表現できなくなってしまったので、適度にリセットしてくれるmodern-css-resetにしました。導入は簡単で、
yarn add modern-css-reset
して、index.jsにimport 'modern-css-reset';
するだけです。Flex,Gridで簡単にレイアウト
App.jsでレイアウトを作っていきます。レイアウトを実現するのにCSSを自分で書く必要はなく、用意されたタグだけで十分でした。
全体の構成は
- ヘッダー
- 検索部分
- ガイダンス部分
- コンテンツ部分
- フッター
です。
全体をFlexboxで並べて、それぞれのレイアウトの調整にさらにFlexboxやGridを使いました。コンテンツ部分は固定サイズで配置したかったのでGridにしています。React Spectrumのイメージがつかめるように、import部分とreturnのレイアウト部分のみ抜粋します。App.jsimport React, { useReducer, useEffect } from 'react'; import './App.css'; import HookedHeader from "./HookedHeader"; import Movie from "./Movie"; import Search from "./Search"; import { Provider, defaultTheme, Flex, View, Text, Grid, repeat, Footer, ProgressCircle, Link } from '@adobe/react-spectrum'; const App = () => { return ( <Provider theme={defaultTheme} colorScheme={colorSchemeStete.colorScheme}> {/* 全体をflexbox化する */} {/* ダークモードでも白地が見えないように画面の高さ分をコンテンツ領域で確保する */} <Flex direction="column" gap="size-100" minHeight="100vh"> {/* ヘッダー部 */} <HookedHeader text="HOOKED" switch={switchColorScheme} currentColorScheme={colorSchemeStete.colorScheme} /> {/* 検索部 中央寄せにする*/} <Flex direction="row" justifyContent="center"> <Search search={searchMethod} /> </Flex> {/* ガイダンス部 中央寄せにする */} <Flex direction="row" justifyContent="center"> <Text>Sharing a fwe of our favourite movies</Text> </Flex> {/* コンテンツ部 Grid化する */} <Grid columns={repeat('auto-fit', 'size-2400')} autoRows="size-2400" justifyContent="center" gap="size-200"> {loading && !errorMessage ? ( // ローディング表示 <View // 上下中央表示 alignSelf="center" // 左右中央表示(gridのrpeatを無視) justifySelf="center"> <ProgressCircle aria-label="Loading…" isIndeterminate /> </View> ) : errorMessage ? ( // エラーメッセージ表示 <View // 左右中央表示(gridのrpeatを無視) justifySelf="center"> <div className="errorMessage">{errorMessage}</div> </View> ) : ( // コンテンツ表示 movies.map((movie, index) => ( <View backgroundColor="gray-200"> <Movie key={`${index}-${movie.Title}`} movie={movie} /> </View> )) )} </Grid> {/* フッター リンクをつけてみる*/} <Footer alignSelf="center"> {/* Linkを使うとダークモードでも見やすく色が変わる */} <Link> <a href="https://www.freecodecamp.org/news/how-to-build-a-movie-search-app-using-react-hooks-24eb72ddfaf7/" target="_blank"> freeCodeCamp:How to build a movie search app using React Hooks </a> </Link> <br /> <Link> <a href="https://react-spectrum.adobe.com/react-spectrum/index.html" target="_blank"> React Spectrum:A React implementation of Spectrum, Adobe’s design system. </a> </Link> </Footer> </Flex> </Provider> ); }; export default App;ちなみにヘッダーコンポーネントも中身の配置を制御するのに、flexbox化しています。
HookedHeader.jsimport React from "react"; import { Header, Heading, View, Flex } from '@adobe/react-spectrum'; import ColorSchemeSwitch from './ColorSchemeSwitch'; const HookedHeader = (props) => { return ( <Header> {/* 背景色 */} <View backgroundColor="red-500"> {/* 文言を中央寄せ */} <Flex direction="row" justifyContent="center"> {/* h2と同等 */} <Heading level="2" > <font color="white">{props.text}</font> </Heading> <View position="absolute" right="size-0"> <ColorSchemeSwitch switch={props.switch} currentColorScheme={props.currentColorScheme}> </ColorSchemeSwitch> </View> </Flex> </View> </Header> ); }; export default HookedHeader;いちいち、classNameをつけて、cssを開いてflexにして、うまくいかないから、divを追加してみて、、、なんてことをしなくても、
<Flex>
タグや<Grid>
タグを並べていけばいいんです!しかも、設定すべき項目はパラメータ化してあるので、directionやjustifyContent、gapなど細かい調整もjsファイルだけでできてしまいます。プロパティの統一感
マニュアルの見やすさからくるものかもしれませんが、コンポーネントに指定できるプロパティが、
- 固有のプロパティ
- Event
- Layout
- Spacing
- Sizing
- Background
- Borders
- Positioning
- Accessibility
- Advanced
などに体系だって定義されています。コンポーネントごとに、利用できる分類は異なりますが、分類の中では基本的に同じ内容のようです(Eventは違う)。すぐに覚えて使えるようになれました。
また、地味にいいのが、
DimensionValue
です。sizeなどを12px
とか1rem
とか指定するのではなく、Spectrumで定義されたDimensionValue
を使って指定します。具体的には、size-0
、size-10
、size-25
・・・といった値です。これらを使っていれば、Spectrum側で、デバイスに合わせた調整などを行ってくれます。指定する側もpxを使うかremを使うかemを使うかいちいち悩まなくて済みますね。同様に色指定もColorValue
があるので簡単に指定できます。文字色の指定は?
使っていて唯一わからなかったのは、文字色を自分で指定したいときです。ここはこの色にしたい!っていう箇所はやっぱり出てくるのではないかと思います。バックグラウンドカラーを変更するプロパティはあったのですが、文字色を変えるプロパティはぱっと見、見当たりませんでした。classを指定してCSSで書くというのは最後の手段のようなので、一旦、
<font>
タグで色を指定したのですが、これだと、ColorValue
も使えませんし、正しいやり方のようには思いません。そもそも、意味なく文字色だけ変えるなってことですかね。おわりに
Ract Spectrumはリリースされたばかりということで、まだ情報が少ないですが、公式のマニュアルが充実しているので特段困ることはなかったです。React歴数時間、CSSフレームワーク等はBootstrapを眺めたことがある程度ですが、非常に簡単でした。CSSを書かなくてよくて、且つ、タグも自然と標準化された書き方になっていくので、ストレスなく書けました。人気がでることに期待です!
参考
React Spectrum
freecodecamp:Build a movie search app using React hooks
- 投稿日:2020-07-26T11:21:12+09:00
Build a movie search app using React hooksをReact Spectrumで作ってみた
React Spectrumがすごい
React Spectrumがすごそうということで、実際に使って開発してみました。React Spectrum自体の設計のクオリティの高さは、Adobe製デザインシステム「React Spectrum」がすごいので紹介したいの記事が詳しいです。内部の作りはさておき、開発者視点で良かったと感じた点は以下です。
- Adobeが開発しているので信頼度が高い
- Componentのプロパティの統一感があり、わかりやすく使いやすい
- Flex/Gridが簡単にかける
- 基本的にタグで書いていく思想?でCSSを書かなくても大体いける。Flex/Gridもタグがある。
- classNameのプロパティは「UNSAFE_className」になっており、説明にも「最後の手段」と。
Build a movie search app using React hooksについて
2020年のフロントエンドマスターになりたければこの9プロジェクトを作れで1つめに紹介されていたプロジェクトです。元のサイトはこちら。フロントエンドマスターを目指した方なら作ってみたことがあるはず!React Spectrumを試すちょうどよい題材として活用させてもらいました。
React Spectrumを使ってカスタマイズ
完成したイメージ
こんな感じです。
(デザインがダサいと感じたら、それはReact Spectrumではなく私のセンスの問題です。)
スマホにするとこんな感じです。
文字サイズとかいい感じに変わってくれるところとか素晴らしいです。
ダークモードにするとこんな感じです。
本来はOSの設定に応じて切り替わりますが、Reactの勉強がてら切り替えボタンをつけてみました。文字色もうまく変えてくれています。
キャプチャではわかりませんが、検索APIを呼び出している間は、ProgressCircleがくるくる回ります。
中身の説明
リセットCSS
リセットCSSがなくても大丈夫かもしれませんが、hタグを使った時の余白が気に入らなかったので、リセットCSSを入れました。使用したのはmodern-css-resetです。はじめは、destyle.cssを使ってみたのですが、hタグで見た目の違いが表現できなくなってしまったので、適度にリセットしてくれるmodern-css-resetにしました。導入は簡単で、
yarn add modern-css-reset
して、index.jsにimport 'modern-css-reset';
するだけです。Flex,Gridで簡単にレイアウト
App.jsでレイアウトを作っていきます。レイアウトを実現するのにCSSを自分で書く必要はなく、用意されたタグだけで十分でした。
全体の構成は
- ヘッダー
- 検索部分
- ガイダンス部分
- コンテンツ部分
- フッター
です。
全体をFlexboxで並べて、それぞれのレイアウトの調整にさらにFlexboxやGridを使いました。コンテンツ部分は固定サイズで配置したかったのでGridにしています。React Spectrumのイメージがつかめるように、import部分とreturnのレイアウト部分のみ抜粋します。App.jsimport React, { useReducer, useEffect } from 'react'; import './App.css'; import HookedHeader from "./HookedHeader"; import Movie from "./Movie"; import Search from "./Search"; import { Provider, defaultTheme, Flex, View, Text, Grid, repeat, Footer, ProgressCircle, Link } from '@adobe/react-spectrum'; const App = () => { return ( <Provider theme={defaultTheme} colorScheme={colorSchemeStete.colorScheme}> {/* 全体をflexbox化する */} {/* ダークモードでも白地が見えないように画面の高さ分をコンテンツ領域で確保する */} <Flex direction="column" gap="size-100" minHeight="100vh"> {/* ヘッダー部 */} <HookedHeader text="HOOKED" switch={switchColorScheme} currentColorScheme={colorSchemeStete.colorScheme} /> {/* 検索部 中央寄せにする*/} <Flex direction="row" justifyContent="center"> <Search search={searchMethod} /> </Flex> {/* ガイダンス部 中央寄せにする */} <Flex direction="row" justifyContent="center"> <Text>Sharing a fwe of our favourite movies</Text> </Flex> {/* コンテンツ部 Grid化する */} <Grid columns={repeat('auto-fit', 'size-2400')} autoRows="size-2400" justifyContent="center" gap="size-200"> {loading && !errorMessage ? ( // ローディング表示 <View // 上下中央表示 alignSelf="center" // 左右中央表示(gridのrpeatを無視) justifySelf="center"> <ProgressCircle aria-label="Loading…" isIndeterminate /> </View> ) : errorMessage ? ( // エラーメッセージ表示 <View // 左右中央表示(gridのrpeatを無視) justifySelf="center"> <div className="errorMessage">{errorMessage}</div> </View> ) : ( // コンテンツ表示 movies.map((movie, index) => ( <View backgroundColor="gray-200"> <Movie key={`${index}-${movie.Title}`} movie={movie} /> </View> )) )} </Grid> {/* フッター リンクをつけてみる*/} <Footer alignSelf="center"> {/* Linkを使うとダークモードでも見やすく色が変わる */} <Link> <a href="https://www.freecodecamp.org/news/how-to-build-a-movie-search-app-using-react-hooks-24eb72ddfaf7/" target="_blank"> freeCodeCamp:How to build a movie search app using React Hooks </a> </Link> <br /> <Link> <a href="https://react-spectrum.adobe.com/react-spectrum/index.html" target="_blank"> React Spectrum:A React implementation of Spectrum, Adobe’s design system. </a> </Link> </Footer> </Flex> </Provider> ); }; export default App;ちなみにヘッダーコンポーネントも中身の配置を制御するのに、flexbox化しています。
HookedHeader.jsimport React from "react"; import { Header, Heading, View, Flex } from '@adobe/react-spectrum'; import ColorSchemeSwitch from './ColorSchemeSwitch'; const HookedHeader = (props) => { return ( <Header> {/* 背景色 */} <View backgroundColor="red-500"> {/* 文言を中央寄せ */} <Flex direction="row" justifyContent="center"> {/* h2と同等 */} <Heading level="2" > <font color="white">{props.text}</font> </Heading> <View position="absolute" right="size-0"> <ColorSchemeSwitch switch={props.switch} currentColorScheme={props.currentColorScheme}> </ColorSchemeSwitch> </View> </Flex> </View> </Header> ); }; export default HookedHeader;いちいち、classNameをつけて、cssを開いてflexにして、うまくいかないから、divを追加してみて、、、なんてことをしなくても、
<Flex>
タグや<Grid>
タグを並べていけばいいんです!しかも、設定すべき項目はパラメータ化してあるので、directionやjustifyContent、gapなど細かい調整もjsファイルだけでできてしまいます。プロパティの統一感
マニュアルの見やすさからくるものかもしれませんが、コンポーネントに指定できるプロパティが、
- 固有のプロパティ
- Event
- Layout
- Spacing
- Sizing
- Background
- Borders
- Positioning
- Accessibility
- Advanced
になどに体系だって定義されています。コンポーネントごとに、利用できる分類は異なりますが、分類の中では基本的に同じ内容のようです(Eventは違う)。すぐに覚えて使えるようになれました。
また、地味にいいのが、
DimensionValue
です。sizeなどを12px
とか1rem
とか指定するのではなく、Spectrumで定義されたDimensionValue
を使って指定します。具体的には、size-0
、size-10
、size-25
・・・といった値です。これらを使っていれば、Spectrum側で、デバイスに合わせた調整などを行ってくれます。指定する側もpxを使うかremを使うかemを使うかいちいち悩まなくて済みますね。同様に色指定もColorValue
があるので簡単に指定できます。文字色の指定は?
使っていて唯一わからなかったのは、文字色を自分で指定したいときです。ここはこの色にしたい!っていう箇所はやっぱり出てくるのではないかと思います。バックグラウンドカラーを変更するプロパティはあったのですが、文字色を変えるプロパティはぱっと見、見当たりませんでした。classを指定してCSSで書くというのは最後の手段のようなので、一旦、
<font>
タグで色を指定したのですが、これだと、ColorValue
も使えませんし、正しいやり方のようには思いません。そもそも、意味なく文字色だけ変えるなってことですかね。おわりに
Ract Spectrumはリリースされたばかりということで、まだ情報が少ないですが、公式のマニュアルが充実しているので特段困ることはなかったです。React歴数時間、CSSフレームワーク等はBootstrapを眺めたことがある程度ですが、非常に簡単でした。CSSを書かなくてよくて、且つ、タグも自然と標準化された書き方になっていくので、ストレスなく書けました。人気がでることに期待です!
参考
React Spectrum
freecodecamp:Build a movie search app using React hooks
- 投稿日:2020-07-26T03:21:31+09:00
【Next.js】Google App Engineに静的ファイルをリリースした
Next.jsのビルドした静的ファイルをGoogle App Engineにリリースする際に少し詰まったので備忘録を残します。
Next.jsで使用してるAPI等は以下を参考にしました。
https://qiita.com/matamatanot/items/1735984f40540b8bdf91
※まだ検証中の部分もあるためyamlファイルに不要なコードもあるかもしれません。
GAEにリリースするために必要なyamlファイル
app.yamlruntime: nodejs12 handlers: - url: /_next/static static_dir: .next/static secure: always - url: /favicon\.ico static_files: favicon.ico upload: favicon\.ico - url: /static static_dir: static secure: always - url: /.* secure: always redirect_http_response_code: 301 script: auto env_variables: HOST: '0.0.0.0'参考:https://cloud.google.com/appengine/docs/standard/nodejs/config/appref?hl=ja
GAEへリリースする際のコマンド
静的ファイルをビルドしてデプロイするだけですね。
※プロジェクトの作成やログイン等は省略しております。
$ yarn build $ gcloud app deploy app.yaml参考:https://cloud.google.com/appengine/docs/flexible/nodejs/testing-and-deploying-your-app?hl=ja
GKEでリリースしたことがあるのですが、それと比べると圧倒的に楽でした…笑
参考:https://qiita.com/arthur_foreign/items/9007695c5ff02dd493cf
他にもGAEのサービスを運用している場合はdispatch.yamlをデプロイする
Next.jsがdefaultのサービスなら特に考えなくてもよいのですが、他にもサービスがある場合は
dispatch.yaml
を設定する必要があります。※ここも地味に詰まりました。
dispatch.yamldispatch: - url: "*/hogefuga" service: hoge-static # ビルドしたCSSやJSを読み込む必要があるため - url: "*/_next/*" service: hoge-static # static配下の画像を読み込む必要があるため - url: "*/static/images/*" service: hoge-staticルーティングによってサービスを切り替える場合は、少し面倒ですがCSSやJSや画像ファイル等もdispatch.yamlで制御してあげる必要があるようでした。
ブラウザのコンソール上に出てたWarning
ブラウザのコンソール上に出てくれていたWarningは以下でした。
Resource interpreted as Stylesheet but transferred with MIME type text/html: "https://hogefuga.come/_next/static/css/styles.xxxxxxxx.chunk.css".