- 投稿日:2021-01-06T23:47:03+09:00
React Redux Hooks公式ドキュメント翻訳(概要編)
Reactアプリケーションの状態管理のためのOSSライブラリである、React Reduxのバージョン7.1.0で追加された、Hooks APIの公式ドキュメントを翻訳していきます。
2021/1/6公開。原文リンクは以下。
・公式ドキュメント(React Redux Hooks):https://react-redux.js.org/api/hooksHooks APIとは
Reactの新しいhooks APIはローカルなコンポーネントのstateをより簡単に扱うことができる関数コンポーネントです。
React Reduxは今回、高階コンポーネント(HOC)の一つである既存のconnect関数の代わりに、いくつかのhook APIを提供します。これらのAPIを使えば、connect関数であなたのコンポーネントをラップすることをせずとも、ReduxのStoreに登録し、Actionを送る(dispatchする)ことが可能になります。
Hooks APIはReact Reduxのバージョン7.1.0で追加されました。
React ReduxアプリでHooks APIを使う
connect関数と同様に、Hooks APIを使う前の準備として、コンポーネントの階層全体でReduxのStoreを使えるように、以下のようにProviderを設定する必要があります。
const store = createStore(rootReducer) ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )この準備が整えばこれから紹介する、React Redux hooks APIの全てをあなたの関数コンポーネントの中で使用することができます。
次回はHooks APIの一つ、useSelector()について説明します。
- 投稿日:2021-01-06T21:08:08+09:00
個人でWebサービス(パズルアプリ)をリリースしたときの話
作ったサービス
概要
オートマトンのパズルアプリです。
お題のオートマトンをユーザが作成していくサービスです。ここでは、動機や使用技術、ユーザの反応を中心に記載していきます。
※詳しいサービスの内容は、WebサービスのURLから見ることができます。プレイイメージ
WebサービスのURL
https://www.loving-automaton.com#/
ソースコード
https://github.com/threeislands/automaton-app
動機
以前、オートマトンに関する本を読んだときに、正規言語とその状態遷移図が記載されていました。
そのときに「正規言語から状態遷移図を作成する過程は、非常に面白いのではないか!?」と思いました。
他の理由
- オートマトンの描画を実装できるか検証したい
- オートマトンの面白さを他の人にも伝えたい!!
そこで、実際につくってみることにしました。
機能概要
- オートマトン描画機能
- オートマトン判定機能
- ログイン機能
- オートマトンの保存/取得機能(ログイン済みのユーザのみ)
技術スタック
概要
今回は、フロントエンドとバックエンドを分離した SPA + BFF の構成にしました。
理由
- これまでMVCフレームワークを使ったWeb開発が多かったのですが、その際にテンプレートエンジン(Thymeleaf)とJavaScript(jQuery)のそれぞれの変数が混在していて、可読性があまり高くないと感じたため
- SPAを採用した場合の認証やログインの実装方法に興味があったため
フロントエンド
React, (D3)
SPAフレームワークには、Reactを採用しました。
また、一部にD3を利用しました。(ドラッグ&ドロップの処理)バックエンド
Flask, SQLAlchemy
バックエンドのAPIサーバのフレームワークに、Flaskを利用しました。
また、ORMには、SQLAlchemyを利用しました。
marshmallowというライブラリを利用することで、簡単にモデル⇔JSONを相互変換できるのが便利でした。インフラ
GitHub Pages
フロントエンド(React)はGitHub Pages上で、運用しています。
GitHub PagesはPublicリポジトリであれば、無料で利用できる点が魅力的でした。
また、gh-pagesというライブラリ利用することで、簡単にデプロイできるのも良いと思いました。AWS (CloudFront, Elastic Beanstalk(EC2))
バックエンド(Flask)はElastic Beanstalk(EC2)上で、運用しています。
CloudFrontにSSL証明書を設定して、HTTPSにしています。クライアント(ブラウザ) - GitHub Pages 間にCloudFrontを配置することで、フロントエンドとバックエンドのoriginを同じにしています。
それによって、CookieにSameSite属性(=Lax)を指定して、CSRFの対策をおこなえるようにしています。(Cookieベースの認証を利用している)インフラ構成
リクエストパスが
/api
から始まる場合、CloudFrontでEC2にリバースプロキシするようにしています。
それ以外のリクエストパスの場合、Github Pagesにリバースプロキシするようにしています。認証
概要
今回は、ログイン機能にGoogleのOpenID ConnectとTwitterのOAuth1.0を利用しました。
Google / OpenID Connect の場合
- 外部サービスのログイン後に、APIサーバにリダイレクトする
- リダイレクト時のパスに含まれるコード値から、id_tokenを取得する外部APIを実行する
- 3. 取得したid_tokenのsubがユーザテーブルに登録されていれば(未登録の場合、新規登録)、SessionにユーザIDを設定して、認証済みのユーザとする
Twitter / OAuth1.0 の場合
- 外部サービスのログイン後に、APIサーバにリダイレクトする
- リダイレクト時のパスに含まれるoauth_token, oauth_verifierから、アクセストークンを取得する外部APIを実行する
- 取得したトークンのuser_idがユーザテーブルに登録されていれば(未登録の場合、新規登録)、SessionにユーザIDを設定して、認証済みのユーザとする
フロントエンド
React
初回ロード時に、ユーザ情報取得のAPIを実行します。
認証済みの場合のみ、ユーザ情報を取得できます。
取得したユーザ情報を、UserContext
に設定して他のコンポーネントから参照できるようにしています。App.jsfunction App() { const [user, setUser] = useState(null); useEffect(() => { const api = async () => { const user = await UserService.getUser(); // 未ログインの場合、空のobjectが返却されるのでnullを設定 if (Object.keys(user).length === 0) { setUser(null); } else { setUser(user); } } api(); }, []); return ( <div className="App"> <CssBaseline/> <Suspense fallback="loading"> <UserContext.Provider value={user}> {/* Other Components... */} </UserContext.Provider> </Suspense> </div> ); )UserContextの参照例(該当箇所のみ抜粋)
ログイン状態に応じて、プロフィール欄とログインボタンの表示を切り替えるHeader.jsfunction Header(props) { const user = React.useContext(UserContext); const profileArea = user ? UserProfile : LoginButton; return ( <Container> <Grid> {profileArea()} {/* Other Components */} </Grid> </Container> }API (axios)
APIを実行するライブラリとして、axiosを利用しています。
リクエストの際にCookieも送付するように、withCredentials
をの設定をします。base-service.jsexport default axiosBase.create({ baseURL: Constant.API_ENDPOINT, headers: { 'Content-Type': 'application/json', }, withCredentials: true, responseType: 'json' });バックエンド
認証状態をチェックするデコレータ
今回は認証用のライブラリを利用せずに自前で実装しました。
Sessionに user_id が設定されている場合、認証済みとしています。authentication_required.pyfrom functools import wraps from flask import session, jsonify def authentication_required(f): @wraps(f) def decorated_function(*args, **kwargs): if session.get('user_id') is None: return jsonify({'message': 'Unauthorized'}), 401 return f(*args, **kwargs) return decorated_function上記デコレータの利用例
作成したオートマトンを保存できるAPI
認証済みのユーザのみ、このAPIを呼び出すことができるuser_api.py@bp.route('/user/save_automaton/<int:question_id>', methods=['POST']) @authentication_required def save_automaton(question_id): user_id = session.get('user_id') req_body = request.json schema = AutomatonSchema() automaton_data = schema.load(req_body) created_automaton = CreatedAutomaton( user_id=user_id, question_id=question_id, automaton_data=schema.dump(automaton_data)) AutomatonService.create(created_automaton) return make_response(jsonify({'message': 'success'}))印象に残っていること
タイトル/デザインについて
当初は、タイトルを「オートマトンの部屋」にして、Mine◯raftのような配色にすることを想定していました。
開発中に、「恋する寄◯虫」という漫画から着想を得て、「恋するオートマトン」が思いつき、語呂がけっこう良かったので採用しました。
デザインもタイトルに合わせて、ピンクと水色を基調としたPOPな配色にしました。
エフェクトについて
当初は、回答時のアニメーションにエフェクトをつけることは予定していませんでした。
しかし、アニメーション実装後に(エフェクトなしで)プレイしてみたところ、爽快感や気持ちよさをあまり感じませんでした。
そこで、正解時に花火のようなエフェクトを設定しました。
多少なりとも、爽快感を感じるようになったと思います。エフェクトのアニメーションはSVGのanimate, animateMotion, animateTransformを利用して実装しました。
CSSの Key frame と異なり、動的にアニメーションの時間や変化量、角度を設定できます。実際のソースは以下で確認できます。
https://github.com/threeislands/automaton-app/blob/master/frontend/src/components/ClearEffect.jsリリース後の反応
リリースして、友人や知り合いなどだいたい20人ぐらいの人にプレイしてもらうことができました。
ポジティブなフィードバック
- 面白い
- 楽しかった
エンジニアやゲームが好きな人、数学好きな人からは好意的なフィードバックをもらうことが多く、とても嬉しかったです。
また、レベル7や8(結構むずかしい問題)でもあっさりとクリアしてしまう人がいたことには、とても驚かされました。どちらかというとネガティブなフィードバック
- 難しすぎる
- 意味分かんない!
- 説明は理解できたけど、どうやっていいかわからない
- 出会い系サイトっぽい
まず、プレイするにあたって理解しておくルールが多いため、ユーザの認知的な負荷が大きくなってしまいました。
改善案として、インタラクティブなオンボーディングを導入することを検討したいです。(ユーザの操作ごとに説明文が更新される等)出会い系サイトっぽいという意見はちょっと意外でした。笑
素敵なオートマトンに出会えるので、ある意味出会い系サイトかもしれません!?感想
今回、初めて個人でサービスを開発してリリースまでおこなうことができました。
自分の開発したサービスをユーザが利用してくれることはとても嬉しく、フィードバックからも多くの洞察を得ることができました。
今回作成したサービスは、自分の作りたいサービスでしたが、次回はユーザの潜在的なニーズに合致するサービスを作りたいと思います。
- 投稿日:2021-01-06T21:08:08+09:00
個人開発でWebサービス(パズルアプリ)をリリースしたときの話
作ったサービス
概要
オートマトンのパズルアプリです。
お題のオートマトンをユーザが作成していくサービスです。ここでは、動機や使用技術、ユーザの反応を中心に記載していきます。
※詳しいサービスの内容は、WebサービスのURLから見ることができます。プレイイメージ
WebサービスのURL
https://www.loving-automaton.com#/
ソースコード
https://github.com/threeislands/automaton-app
動機
以前、オートマトンに関する本を読んだときに、正規言語とその状態遷移図が記載されていました。
そのときに「正規言語から状態遷移図を作成する過程は、非常に面白いのではないか!?」と思いました。
他の理由
- オートマトンの描画を実装できるか検証したい
- オートマトンの面白さを他の人にも伝えたい!!
そこで、実際につくってみることにしました。
機能概要
- オートマトン描画機能
- オートマトン判定機能
- ログイン機能
- オートマトンの保存/取得機能(ログイン済みのユーザのみ)
技術スタック
概要
今回は、フロントエンドとバックエンドを分離した SPA + BFF の構成にしました。
理由
- これまでMVCフレームワークを使ったWeb開発が多かったのですが、その際にテンプレートエンジン(Thymeleaf)とJavaScript(jQuery)のそれぞれの変数が混在していて、可読性があまり高くないと感じたため
- SPAを採用した場合の認証やログインの実装方法に興味があったため
フロントエンド
React, (D3)
SPAフレームワークには、Reactを採用しました。
また、一部にD3を利用しました。(ドラッグ&ドロップの処理)バックエンド
Flask, SQLAlchemy
バックエンドのAPIサーバのフレームワークに、Flaskを利用しました。
また、ORMには、SQLAlchemyを利用しました。
marshmallowというライブラリを利用することで、簡単にモデル⇔JSONを相互変換できるのが便利でした。インフラ
GitHub Pages
フロントエンド(React)はGitHub Pages上で、運用しています。
GitHub PagesはPublicリポジトリであれば、無料で利用できる点が魅力的でした。
また、gh-pagesというライブラリ利用することで、簡単にデプロイできるのも良いと思いました。AWS (CloudFront, Elastic Beanstalk(EC2))
バックエンド(Flask)はElastic Beanstalk(EC2)上で、運用しています。
CloudFrontにSSL証明書を設定して、HTTPSにしています。クライアント(ブラウザ) - GitHub Pages 間にCloudFrontを配置することで、フロントエンドとバックエンドのoriginを同じにしています。
それによって、CookieにSameSite属性(=Lax)を指定して、CSRFの対策をおこなえるようにしています。(Cookieベースの認証を利用している)インフラ構成
リクエストパスが
/api
から始まる場合、CloudFrontでEC2にリバースプロキシするようにしています。
それ以外のリクエストパスの場合、Github Pagesにリバースプロキシするようにしています。認証
概要
今回は、ログイン機能にGoogleのOpenID ConnectとTwitterのOAuth1.0を利用しました。
Google / OpenID Connect の場合
- 外部サービスのログイン後に、APIサーバにリダイレクトする
- リダイレクト時のパスに含まれるコード値から、id_tokenを取得する外部APIを実行する
- 3. 取得したid_tokenのsubがユーザテーブルに登録されていれば(未登録の場合、新規登録)、SessionにユーザIDを設定して、認証済みのユーザとする
Twitter / OAuth1.0 の場合
- 外部サービスのログイン後に、APIサーバにリダイレクトする
- リダイレクト時のパスに含まれるoauth_token, oauth_verifierから、アクセストークンを取得する外部APIを実行する
- 取得したトークンのuser_idがユーザテーブルに登録されていれば(未登録の場合、新規登録)、SessionにユーザIDを設定して、認証済みのユーザとする
フロントエンド
React
初回ロード時に、ユーザ情報取得のAPIを実行します。
認証済みの場合のみ、ユーザ情報を取得できます。
取得したユーザ情報を、UserContext
に設定して他のコンポーネントから参照できるようにしています。App.jsfunction App() { const [user, setUser] = useState(null); useEffect(() => { const api = async () => { const user = await UserService.getUser(); // 未ログインの場合、空のobjectが返却されるのでnullを設定 if (Object.keys(user).length === 0) { setUser(null); } else { setUser(user); } } api(); }, []); return ( <div className="App"> <CssBaseline/> <Suspense fallback="loading"> <UserContext.Provider value={user}> {/* Other Components... */} </UserContext.Provider> </Suspense> </div> ); )UserContextの参照例(該当箇所のみ抜粋)
ログイン状態に応じて、プロフィール欄とログインボタンの表示を切り替えるHeader.jsfunction Header(props) { const user = React.useContext(UserContext); const profileArea = user ? UserProfile : LoginButton; return ( <Container> <Grid> {profileArea()} {/* Other Components */} </Grid> </Container> }API (axios)
APIを実行するライブラリとして、axiosを利用しています。
リクエストの際にCookieも送付するように、withCredentials
をの設定をします。base-service.jsexport default axiosBase.create({ baseURL: Constant.API_ENDPOINT, headers: { 'Content-Type': 'application/json', }, withCredentials: true, responseType: 'json' });バックエンド
認証状態をチェックするデコレータ
今回は認証用のライブラリを利用せずに自前で実装しました。
Sessionに user_id が設定されている場合、認証済みとしています。authentication_required.pyfrom functools import wraps from flask import session, jsonify def authentication_required(f): @wraps(f) def decorated_function(*args, **kwargs): if session.get('user_id') is None: return jsonify({'message': 'Unauthorized'}), 401 return f(*args, **kwargs) return decorated_function上記デコレータの利用例
作成したオートマトンを保存できるAPI
認証済みのユーザのみ、このAPIを呼び出すことができるuser_api.py@bp.route('/user/save_automaton/<int:question_id>', methods=['POST']) @authentication_required def save_automaton(question_id): user_id = session.get('user_id') req_body = request.json schema = AutomatonSchema() automaton_data = schema.load(req_body) created_automaton = CreatedAutomaton( user_id=user_id, question_id=question_id, automaton_data=schema.dump(automaton_data)) AutomatonService.create(created_automaton) return make_response(jsonify({'message': 'success'}))印象に残っていること
タイトル/デザインについて
当初は、タイトルを「オートマトンの部屋」にして、Mine◯raftのような配色にすることを想定していました。
開発中に、「恋する寄◯虫」という漫画から着想を得て、「恋するオートマトン」が思いつき、語呂がけっこう良かったので採用しました。
デザインもタイトルに合わせて、ピンクと水色を基調としたPOPな配色にしました。
エフェクトについて
当初は、回答時のアニメーションにエフェクトをつけることは予定していませんでした。
しかし、アニメーション実装後に(エフェクトなしで)プレイしてみたところ、爽快感や気持ちよさをあまり感じませんでした。
そこで、正解時に花火のようなエフェクトを設定しました。
多少なりとも、爽快感を感じるようになったと思います。エフェクトのアニメーションはSVGのanimate, animateMotion, animateTransformを利用して実装しました。
CSSの Key frame と異なり、動的にアニメーションの時間や変化量、角度を設定できます。実際のソースは以下で確認できます。
https://github.com/threeislands/automaton-app/blob/master/frontend/src/components/ClearEffect.jsリリース後の反応
リリースして、友人や知り合いなどだいたい20人ぐらいの人にプレイしてもらうことができました。
ポジティブなフィードバック
- 面白い
- 楽しかった
エンジニアやゲームが好きな人、数学好きな人からは好意的なフィードバックをもらうことが多く、とても嬉しかったです。
また、レベル7や8(結構むずかしい問題)でもあっさりとクリアしてしまう人がいたことには、とても驚かされました。どちらかというとネガティブなフィードバック
- 難しすぎる
- 意味分かんない!
- 説明は理解できたけど、どうやっていいかわからない
- 出会い系サイトっぽい
まず、プレイするにあたって理解しておくルールが多いため、ユーザの認知的な負荷が大きくなってしまいました。
改善案として、インタラクティブなオンボーディングを導入することを検討したいです。(ユーザの操作ごとに説明文が更新される等)出会い系サイトっぽいという意見はちょっと意外でした。笑
素敵なオートマトンに出会えるので、ある意味出会い系サイトかもしれません!?感想
今回、初めて個人でサービスを開発してリリースまでおこなうことができました。
自分の開発したサービスをユーザが利用してくれることはとても嬉しく、フィードバックからも多くの洞察を得ることができました。
今回作成したサービスは、自分の作りたいサービスでしたが、次回はユーザの潜在的なニーズに合致するサービスを作りたいと思います。
- 投稿日:2021-01-06T15:59:55+09:00
[React]refのchildrenから特定のclassNameを持つElementを取得する
ライブラリのDOM触るときとかに使うのでメモ
import React, { useRef } from 'react' import styled from 'styled-components' export default function Foo() { const ref = useRef<HTMLDivElement>(null) React.useEffect(() => { const element = Array.from(ref.current?.children || []).find( (item) => item.className === 'some-class-name' ) ... }, [ref]) return ( <> <SomeElement ref={ref} /> </> ) const SomeElement = styled.div`` }
- 投稿日:2021-01-06T15:48:03+09:00
Blitz.jsの議論から学ぶ,formik vs react-final-form vs react-hook-form
What should the default form library be in Blitz apps?
BlitzがどのFormライブラリをデフォルトにするかの議論が参考になったので要約しました。
の3択で,結論としては,React Final Formを推奨とする形に落ち着きました。
最初のコメント
Blitz.jsの作者であるBrandon(@flybayer)さんがコメントしています。要約するとこんな感じ。
Formik
以前使ってたけど,パフォーマンスめっちゃ悪いって気づいたから,Formikは無しで。React-final-form
最近ずっと使ってるけどいい感じ。React-hook-form
ほとんど使ったことない。React-final-formをReact-hook-formに書き換えてみたが,コードの複雑さとしてはそこまで差異はない。
https://github.com/blitz-js/blitz/pull/767/files議論
全体的にFormikは無いよねという感じでした。
気になった意見をpick upします。
react-hook-formのデメリット
対象のコメント→ https://github.com/blitz-js/blitz/discussions/768#discussioncomment-39148
別のFormライブラリとの互換性の低さ
MaterialUIなどのサードパーティー製のコンポーネントと一緒に使う場合は
<Controller>
コンポーネントでwrapしなくてはいけない。<Controller name="iceCreamType" as={Select} options={[ { value: "chocolate", label: "Chocolate" }, { value: "strawberry", label: "Strawberry" }, { value: "vanilla", label: "Vanilla" } ]} control={control} rules={{ required: true }} />
<Controller>
を使わないパターンと使うパターンの2つの異なるAPIを知っておかなくてはいけない。一方,react-final-formは
useField()
というAPIだけで済むので,他のライブラリとの互換性が高い。refの渡し方が不自然
focusしたときに何かを発動したいときなどはrefを自分で渡すことになるが,そのときのrefの渡し方が面倒くさい。
const { register, handleSubmit } = useForm(); const firstNameRef = useRef(); const onSubmit = data => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="firstName" ref={(e) => { register(e) // hook-form register firstNameRef.current = e // manually assign your ref }} />
ref={ref}
で渡したいのに…。react-final-formのデメリット
render propsが初心者には難しいのではないか?というコメントがいくつかあった。
- 基本的にrender propsを使う
- react-final-form
- formik
- 基本的にrender propsを使わない
- react-hook-form
それに対し,Brandonさんは以下のように反論しています。
there is only one top level render prop with final-form which also provides the form context.
トップ層でしか使用していないし,
So either way, you'll likely have a render prop. I definitely agree the primary API shouldn't be a render prop, but looks like it's impossible to 100% avoid render props.
react-hook-formでも
<Controller/>
を使ったらrender propsを使うことになるし,render propsから完全に逃れることはできないのではないか?とのことです。まとめ
今回はReact Final Formを推奨とする結論になりましたが,「とにかく速く開発したいからみんなが使えて学習コストの低いreact hook formで」など,プロジェクトによって吟味する必要がありそうです。
「react-hook-formは広く使われているからみんな慣れてるだろうし良い」というような意見に対して「学習コストが低いものを選定基準にするのではなく,既存のプロジェクトをBlitzに置き換えることになったときになるべく痛みが少ないものを使いたい。」と返していて,ライブラリ選定の参考になる見方だと思いました。
追記
React Hook Formの作者である@bluebill1049さんがコメントしてくださりました。(詳しくは本人のコメントを参照してください!)
2020年12月ごろにリリースされたv6.13.0で
useController
というhooksが追加されました。それを使えば<Controller>
で囲ってRender prop
を使うという必要がなくなります。Material UIのコンポーネントなどを使う場合は<Controller>
を使うしかありませんが,自作のコンポーネントを使う場合はuseController
が使えます!また,
ref
についてもV7ではフォーカス管理を改善していく予定とのことです。
- 投稿日:2021-01-06T06:14:42+09:00
ReactでCan't perform a React state update on an unmounted component.の原因と解決
プログラミング開始7ヶ月目の者です。
ReactのクラスコンポーネントのcomponentDidMountについてです。
メソッド内でsetTimeoutの記述をしました。
// 表示される毎に実行 componentDidMount() { setTimeout(() => { this.setState({ isVisible: !this.state.isVisible }); }, 1000); }それでsetTimeoutで値が切り替わる前に別のLink(Router)に飛ぶとエラーが出ました。
エラー
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
原因
componentDidMountしたのにunmountedされたまま、別のLinkに移った為です。
「マウントされないcomponentでstateを更新しようとしています。componentWillUnmountでキャンセルしてね。」解決
componentWillUnmount用に変数とメソッドを追加し代入する。
これで別のページに移ったときにcomponentWillUnmountでキャンセルされます。// unmount用の変数 handle; // 変数に代入 componentDidMount() { this.handle = setTimeout(() => { this.setState({ isVisible: !this.state.isVisible }); }, 1000); } // unmount用メソッドを追加 引数に変数を入れる。 componentWillUnmount() { clearTimeout(this.handle); }
修正サンプル。
const props = { visible: { opacity: 1, }, hidden: { opacity: 0, }, } const Box = posed.div(props) export default class Can extends React.Component { handle; state = { isVisible: false, }; // mount componentDidMount() { this.handle = setTimeout(() => { this.setState({ isVisible: !this.state.isVisible }); }, 1000); } // unmount componentWillUnmount() { clearTimeout(this.handle); } render() { return ( <Box pose={this.state.isVisible ? 'visible' : 'hidden'}>出現</Box> ); } }参考
エラーで悩んでいた時に見つけた 記事 です。
以上です!
- 投稿日:2021-01-06T01:09:50+09:00
Reactを書くときのコマンドが覚えられない話
Reactを書くときのコマンド,多くないですか?
前提
OS:MacOS Catalina (sedコマンドを使うので,Mac以外だと動かんかも)
ビルドツール:Parcel (npm install -g parcel
)
Shell: Fish
必要なコマンド:gron (brew install gron
)経緯
最近Reactの勉強を始めたのですが,プロジェクトごとに使うコマンドが多くて覚えられません...
最初はReactとBabelくらいだったのでなんとかなっていましたが,プラグインなどが入ってきてどれがどれだかわからなくなってしまいました.
そこで,「プラグインとかだいたい使うの一緒だしシェルスクリプトにすればいいのでは!?!?」と思ったのでシェルスクリプトにしました.TL;DR
react-init.fishfunction react-init npm init npm install --save react react-dom npm install --save-dev babel-preset-react-app npm install --save-dev react-hot-loader mkdir -p src/js touch ./src/index.html echo '{ "presets": ["react-app"], "plugins": ["react-hot-loader/babel"] }'>.babelrc cat package.json|gron|sed 's/test/start/'|sed -E 's/"echo.*/"parcel .\/src\/index.html";/'|gron -u>package.json.bak rm package.json mv package.json.bak package.json endコード解説
Reactのプロジェクトを立ち上げるのに使いたいコマンドは
npm init npm install --save react react-dom npm install --save-dev babel-preset-react-app npm install --save-dev react-hot-loaderの4つなので,これをシェルスクリプトに書きます.
次に,JavaScriptを入れておくディレクトリとindex.htmlを作成します.
mkdir -p src/js touch src/index.html次に,
.babelrc
を作成します.
react-hot-loaderを使わない場合は,pluginsは削除してください.echo '{ "presets": ["react-app"], "plugins": ["react-hot-loader/babel"] }'>.babelrc
npm init
で作成されたpackage.json
にparcelのコマンドを追加していきます.
もともとnpm run test
用のコマンドが書いてあるので,それを置換していきます.余談ですが,gronコマンドはjsonをgrepしやすい形にしてくれるコマンドです.
初めて使いましたがかなり便利でした.cat package.json|gron|sed 's/test/start/'|sed -E 's/"echo.*/"parcel .\/src\/index.html";/'|gron -u>package.json.bak rm package.json mv package.json.bak package.jsonおわりに
調べてもこんなことしてる記事が見つからなかったので,絶対間違ってるんだろうなという感じです.
実際に開発するときにはpluginとかが変わってくるからやらないのかな?などと思いました.