- 投稿日:2020-09-29T20:09:24+09:00
formikとreact-hook-formの比較-フォームを動的に増やす実装
目的
formikとreact-hook-formで動的に項目を増減させる書き方を調べたのでメモ。
主に以下の機能についての検証。
- formikのFieldArray
- react-hook-formのuseFieldArray
前提
- Visual Studio Code 1.49.2
- react 16.13.1
- typescript 3.7.5
- formik 2.1.5
- react-hook-form 6.8.6
- 姓、名の2つのデータを複数入力できるようにする。
- 追加ボタンで姓、名の入力欄を増やす事ができるようにする。
サンプルデータ
formのinputイメージ
const initialValues = { listName: "", table: { rows: [ { firstName: "sato", lastName: "taro" }, { firstName: "suzuki", lastName: "jiro" } ] } };最終的に作ったもの
https://codesandbox.io/s/exciting-chatterjee-n68j3
上半分がformikの実装サンプルで、
下半分がreact-hook-formのサンプル。ざっくり結論
formikはtypescriptの型がanyになってしまう。componentの定義もツラかった。
react-hook-formなら型定義できるが、hookの使い方が複雑。
typescriptの利用を前提にした場合、個人的な好みはreact-hook-form。内容
1. formikのFieldArrayについて
form周りはどうしても複雑になりがちですが、
tutorialでとても分かりやすく使い方を定義してくれています。
https://formik.org/docs/tutorialそんなformik公式ページですが、FieldArrayの解説になると分かりにくい点が多いです。
https://formik.org/docs/api/fieldarrayimport React from 'react'; import { Formik, Form, Field, FieldArray } from 'formik'; export const FriendList = () => ( <div> <h1>Friend List</h1> <Formik initialValues={{ friends: ['jared', 'ian', 'brent'] }} // renderはDeprecatedでは? render={({ values }) => ( <Form> <FieldArray name="friends" // ここでもrender render={arrayHelpers => ( <div> {values.friends && values.friends.length > 0 ? ( values.friends.map((friend, index) => ( // 省略 )) )} <div> <button type="submit">Submit</button> </div> </div> )} /> </Form> )} /> </div> );※公式サンプルを一部省略し、コメントを追加
2系ではDeprecatedとなっているはずのrender関数が使われています。
しかも2回も。
そのため、componentが分離できず階層が深くて見通しが悪い実装になっています。しかし、処理を追っていくとrenderの必要性が薄れてきます。
Formikタグのrenderはvaluesを、FieldArrayタグのrenderはarrayHelpersを取得するためのようですが、公式ページを読み進めると次のような書き方も提示されています。import React from 'react'; import { Formik, Form, Field, FieldArray } from 'formik' export const FriendList = () => ( <div> <h1>Friend List</h1> <Formik initialValues={{ friends: ['jared', 'ian', 'brent'] }} onSubmit={...} render={formikProps => ( <FieldArray name="friends" component={MyDynamicForm} /> )} /> </div> ); // In addition to the array helpers, Formik state and helpers // (values, touched, setXXX, etc) are provided through a `form` // prop export const MyDynamicForm = ({ move, swap, push, insert, unshift, pop, form }) => ( <Form> {/** whatever you need to do */} </Form> );※公式サンプルをそのまま貼り付け
ポイントは以下の3点です。
- FieldArrayのpropsにcomponentを設定できる。
- 上記のcomponentには引数でhelpersを受け取る。
- helpers.form.valuesとたどればvaluesが取得できる
どうやらrenderで取得せずともvaluesやhelperを取得できるらしい。
ということで、FieldArrayでcomponentを使って実装してみました。2. formikのFieldArrayとcomponentを使った実装
ただ、実装してみると、思ったほどうまくいきません。
typescript周りで面倒な事が起こります。実装のコメントにも書いていますが、componentに渡すFCの定義がエラーになってしまいます。
// 親コンポーネント const FormikPart: React.FC = () => { return ( <> <h2>Formik array</h2> <Formik initialValues={initialValues} onSubmit={(values, helper) => { console.log(values); }} > <Form> <FieldArray name="table.rows" component={ArrayInput} /> <button type="submit">submit</button> </Form> </Formik> </> ); }; // 子コンポーネント // <void | FieldArrayRenderProps>を受け取れないとerrorになる const ArrayInput: React.FC<void | FieldArrayRenderProps> = (props) => { // propsの型がReact.PropsWithChildren<void | FieldArrayRenderProps>になってしまうので // as で型を定義する const { form, name, push } = props as React.PropsWithChildren< FieldArrayRenderProps >; // name = table.rows // getInを使うと"table.rows"をハードコードする場所が減らせる // table.rowsの配列の値は取得できるが、型はany const array = getIn(form.values, name); // 型がanyなのでチェック等を書く必要がある if (!(Array.isArray(array) && array.length > 0)) return null; const list = array.map((val, index) => ( <div key={index}> <Field name={`${name}[${index}].firstName`} type="text" /> <Field name={`${name}[${index}].lastName`} type="text" /> </div> )); return ( <> {list} <button type="button" // pushの引数も型もanyなので型チェックはされない onClick={() => push({ firstName: "tanaka", lastName: "saburo" })} > add at last </button> </> ); };コード全体が見たい方はこちら
https://codesandbox.io/s/exciting-chatterjee-n68j3?file=/src/components/FormikPart.tsx
FieldArrayRenderProps
はformikから提供されているFieldArrayの引数の型です。
本来ならReact.FC<FieldArrayRenderProps>
のようにFCを定義したいのですが、
propsのcomponentの型定義がReact.ComponentType<T | void>
となっておりvoidの指定をしないとtypescriptから怒られてしまいます。
React.ComponentType<void>
という定義にどんな意味があるのかはよくわかりませんが...仕方なく上記のように型を定義し、引数のpropsはasで型を再度定義します。また、formから取得した入力値も型がanyになってしまい、firstNameやlastNameといったオブジェクト構造のインテリセンスも効かないため、気をつけて実装する必要があります。
こうしたtypescriptの型定義の難しさを避けるために、別のライブラリも試してみることにしました。
3. react-hook-formのuseFieldArrayを使った実装
typescriptでよりシンプルに書くために、新しいライブラリであるreact-hook-formにトライしました。
https://react-hook-form.com/jp/
react-hook-formのドキュメントはとてもオシャレです。
formikのFieldArrayに相当するのはuseFieldArrayというhookです。https://react-hook-form.com/jp/api#useFieldArray
必要な関数は最初からhookで提供されているので、renderの引数やcomponentの分離といったことは考えなくて大丈夫でした。
// 親コンポーネント const ReactHookFormPart: React.FC = () => { const methods = useForm<FormInputs>({ defaultValues }); const onSubmit = (data: FormInputs) => console.log(data); return ( <> <h2>React Hook Form array</h2> <FormProvider {...methods}> <form onSubmit={methods.handleSubmit(onSubmit)}> <ArrayInput /> <button type="submit">submit</button> </form> </FormProvider> </> ); }; // 子コンポーネント const name = "table.rows"; const ArrayInput: React.FC = () => { // hook利用時に型を指定する必要はあるが、型定義ができる const { control, register } = useFormContext<FormInputs>(); // useFieldArrayでは配列の要素の型を指定する const { fields, append } = useFieldArray<TableRows>({ control, // name(table.rows)を指定することで,fieldsにtable.rowsの値が入る name }); const list = fields.map((item, index) => ( <div key={item.id}> <input name={`${name}[${index}].firstName`} // registerはregister()としてmap内で実行する必要がある ref={register()} // itemも型定義されている defaultValue={item.firstName} /> <input name={`${name}[${index}].lastName`} ref={register()} defaultValue={item.lastName} /> </div> )); return ( <> {list} <button type="button" // appendの引数も型定義されている onClick={() => append({ firstName: "tanaka", lastName: "saburo" })} > add at last </button> </> ); }; export default ReactHookFormPart;コード全体が見たい方はこちら
https://codesandbox.io/s/exciting-chatterjee-n68j3?file=/src/components/ReactHookFormPart.tsx子コンポーネントでhooksの利用時に方を指定する必要がありますが、型定義済みの入力値を取得できます。
配列を操作する関数にも型定義がされているため、要素を追加する際にオブジェクトの構造を間違えてしまうこともなさそうです。しかし、registerの使い方やuseForm、useFormContext、useFieldArrayのhook3段活用は、正直理解するのに時間がかかりました(使い方が正しいのかちょっと自信がありません...)。
全体的に簡単そうに見えて意外と難しい、というのがreact-hook-formの印象です。終わり
formik
難しそう... → 意外とカンタンかも → typescriptがerrorとかanyとかreact-hook-form
なんかオシャレ → なんか難しい → なんか難しい(けどtypescriptエラーはない)という印象でした。個人的にはtypescriptで書けることを重視しているのでひとまずreact-hook-formを使おうかと考えています。
ただ、formikもシンプルなフォームであればシンプルに書けるし、FieldArrayもrenderなしで書けるようにアップデートされて上記の悩みがなくなっていくかもしれません。
そもそもformikもreact-hook-formも詳しいわけではないので、もっと良い書き方があれば教えていただけると嬉しいです。
- 投稿日:2020-09-29T19:31:45+09:00
React.js で SVGファイルのサイズや色をCSSで変える方法
前提
- React.js, webpack
- ReactでSVGファイルをimgとして読むとスタイルをCSSで変更できない(サイズはできるが色はできない)のでinlineで読む必要がある
- 複数のサイズや色のために複数のSVGファイルを用意したくない
- SVGファイルを変換したReact Componentのファイルも作りたくない
概要
- inline SVGとして埋め込む方法3つ
- CSSでスタイルを上書きする方法
を紹介
利点としてはSVGをReact Component化せずそのまま使うことで、(デザイナーの用意した)素材を極力そのままにし、ファイル数も増やさずに色を変えることができるSVGの埋め込み方
1. raw-loader + dangerouslySetInnerHTML の場合
webpack.config.jstest: /\.svg$/i, loader: 'raw-loader',jsximport Hoge from '... .svg' ... <div dangerouslySetInnerHTML={{ __html: Hoge }} />typescriptならパスを通したglobal型置き場に次のように書く
global.d.tsdeclare module '*.svg' { const content: string; export default content; }利点
- 変換も何もされないので詰まったり失敗しない
欠点
- dangerously
2. react-svg-loader の場合
https://github.com/boopathi/react-svg-loader/tree/master/packages/react-svg-loader を使う
webpack.config.js{ test: /\.svg$/, use: [ "babel-loader", { loader: 'react-svg-loader', options: { svgo: { plugins: [ { removeViewBox: false }, // to enable overwriteing width/height by CSS { moveElemsAttrsToGroup: false }, // to prevent attribute destruction for overwriting color by CSS ], floatPrecision: 2, }, }, }, ], },ここでsvgoのオプションをdisableしているのは自分の扱うsvgが壊れないように設定した項目だが、扱うsvgによって別のdisableが必要かもしれない
jsximport Hoge from '... .svg' ... <Hoge />typescriptならパスを通したglobal型置き場に次のように書く
global.d.tsdeclare module '*.svg' { const content: React.ComponentType; export default content; }利点
- 巨大ツールでない
- 噛ませるbabel-loaderを自由に指定できる
- next.config.js の
defaultLoaders.babel
とか欠点
- 最近メンテされてないっぽくてリリースがない
- svgoを無効化できない
- SVGの中身によっては更にsvgoのオプションをdisableしないとスタイルを上手く上書きできないかもしれない
3. svgr の場合
https://github.com/gregberge/svgr を使う以外のコードの部分はreact-svg-loaderと同じなので差分だけ書く
- https://react-svgr.com/docs/webpack/ に従う
svgr.config.js
を設定するsvgr.config.jsmodule.exports = { svgoConfig: { plugins: { removeViewBox: false, // to enable overwriteing width/height by CSS moveElemsAttrsToGroup: false, // to prevent attribute destruction for overwriting color }, }, };最適化不要、またはファイルに直接かけておく方針なら、svgoを無効化してしまう
svgr.config.jsmodule.exports = { svgo: false, };利点
- メンテ・リリースが続いている
- svgoを無効化できる
- 破壊的変更のせいでうまくいかなかったら無効化で逃げれる
- star多し
- webpack以外にも対応
欠点
- 依存ライブラリがちょっと多そう
スタイルの当て方
埋め込んだSVGやそのラッパーdivに対しCSSで以下のように書けばサイズや色を変えれる
svg { width: 70px; height: 70px; } svg * :not([stroke='none' i]) { stroke: red; } svg * :not([fill='none' i]) { fill: red; }ポイントは、SVGの色指定はstrokeとfillがあるので、それらがnoneでない箇所を上書きする点
所感
- サクッと実現するならraw-loader + dangerouslySetInnerHTML
- キレイにimportして埋め込みたいなら svgr
- 2つのツールがデフォルトで使うsvgoのデフォルト設定がSVGを破壊的に変更するので曲者
@svgr/cli
などを使ってSVG用のReact Componentを作ってPropsで柔軟に中身を変えるのもアリだけど、SVG素材とReact Componentでほぼ重複するのも悲しいし、用意された素材との同期などが少し面倒参考
- https://blog.kwst.site/201908203549/
- https://github.com/boopathi/react-svg-loader/tree/master/packages/react-svg-loader
- https://github.com/boopathi/react-svg-loader/issues/295
- https://github.com/boopathi/react-svg-loader/issues/261
- https://github.com/boopathi/react-svg-loader/issues/303
- https://github.com/gregberge/svgr
- https://react-svgr.com/docs/webpack/
- https://github.com/svg/svgo
- 投稿日:2020-09-29T18:46:51+09:00
Cognitoで学ぶ認証・認可 in AWS
この記事について
Webアプリのアクセス制御を行いたい!となったときに学ぶべきなのは認証・認可の仕組みです。
AWSにはAmazon Cognitoというユーザー管理を行うための仕組みが存在し、これを利用すれば「実装するだけなら」簡単にアプリのアクセス制御を行うことができます。この記事では「Cognitoが実際に何をやってくれているのか?」というところまで掘り下げながら、簡単なReactアプリを作っていきます。
アジェンダ
- Cognitoのユーザープールを作って触ってみる
- Reactアプリに認証の仕組みを入れてみる
- Cognitoで認証済みの人だけが叩けるAPIをLambda + API Gatewayで作る
- CognitoのIDプールを作り、AWSでの認可の仕組みを学ぶ
- Cognito IDプールで認可された人だけが叩けるAPIをLambda + API Gatewayで作る
使用する環境・バージョン
- React: 16.13.1
- aws-amplify: 3.3.1
- aws-amplify-react: 4.2.5
- @aws-amplify/ui-react: 0.2.21
読者に要求する前提知識
- Reactについての知識(本記事ではReactそのものの説明についてはしません)
- REST APIが何かがわかること
- (Lambda + API GatewayでのREST APIの作り方がわかること)
- IAMロールが何かがわかり、自分で作成できること
Cognitoユーザープールの作成
まずは、AWSウェブコンソールからユーザープールを作成してみましょう。
ユーザープール作成
任意のプール名をつけたあと、設定に進みます。今回は「ステップにしたがって設定」を選択します。
認証(SignIn)時に要求する情報を「(ユーザーが決めた)ユーザー名」にするか「メールアドレスor電話番号」にするかの選択です。
今回は、メールアドレスのみを使用するようにしました。
ユーザー作成(SignUp)時に、ユーザーが登録する必須項目を選択します。
今回の場合、メールアドレスの他に「name」を登録してもらうことにしました。
パスワード関連の設定を行います。
- パスワードの強度を設定: 数字・特殊文字・大文字小文字を要求するか・長さを決めます。
- 自己サインアップ: ユーザー作成をAWSの管理画面からのみにするか、一般ユーザーもアプリ画面等から可能にするかを決めます。
- パスワード有効期限: Sign Up時に発行される、初回ログインのための仮パスワードの有効期限を設定します。
- MFA(多要素認証)の設定: offにするかonにするかを選択します。今回は簡略化のためなし。
- パスワード忘れの時の回復手段: ユーザーがパスワードを忘れたときには、登録済みのメールアドレスから回復させるようにした。
- ロールの提供: Sign Up時やパスワード忘れからの復旧のときに、Cognitoがメールを送るためのロールを付与する。
他の設定は全てデフォルトのままにして、プールを作成します。
ここで確認できるプールIDは控えておきましょう。参考:[新機能] Amazon Cognito に待望のユーザー認証基盤「User Pools」が追加されました!
アプリクライアントの作成
ユーザープールを使う方のアプリの登録・ID払い出しを行います。
アプリを作る際に「クライアントシークレットを作る」のチェックを外しておきましょう。
- 有効なIDプロバイダ: すべて選択をチェック
- コールバックURL: httpsかlocalhostを選択。ここでは暫定的にamazonを指定しておく。
- 許可されているOAuthフロー: Authorization code grantとImplicit grantを選択
- 許可されているOAuthスコープ: 全て選択
ここで作ったアプリクライアントIDもいずれ使うので控えておきます。
Amazon Cognitoドメインを設定
アプリの統合→ドメイン名のタブを開くと、以下のような画面になる。
Amazon Cognitoドメインは、1つのリージョンの中で一意なドメインを設定する必要があります。
まだ使われていなさそうなドメインを「ドメインのプレフィックス」に入力・設定を保存します。動かせるか確認
まずは、「ユーザーとグループ」のタブから、ユーザーを一つ作成しておきます。
OAuthフロー"Implicit Grant"の使用
アプリクライアントの作成のところで、"Implicit Grant"を許可しているので、まずはそれを試してみます。
以下のURLにアクセスします。https://[amazon cognitoドメイン]/login?response_type=token&client_id=[アプリクライアントID]&redirect_uri=[コールバックURL]すると以下のように、Cognitoが提供するサインインフォームが表示されます。
ここで、先ほど作ったユーザーのメールアドレスとパスワードを入力してSign Inすると、コールバックURLにリダイレクトします。
この時のリダイレクト先URLハッシュの中に「IDトークン」「アクセストークン」が格納されています。https://[コールバックURL]#id_token={IDトークン}&access_token={アクセストークン}&expires_in=3600&token_type=Bearerこの後の流れとしては、このURLハッシュで得られたIDトークンとアクセストークンをフロントエンド側で取得・保存し、アプリ内で利用することになります。
参考:【AWS】これだけ見れば理解できるCognito〜認証機能つきサーバレスアーキテクチャの作成〜
OAuthフロー"Authorization Code Grant"の使用
もう一つのOAuthフロー"Authorization Code Grant"も試してみます。
以下のURLにアクセスします。https://[amazon cognitoドメイン]/login?response_type=code&client_id=[アプリクライアントID]&redirect_uri=[コールバックURL]先ほどの"Implicit Grant"の違いは
response_type
が"code"か"token"かというところです。アクセスしたら、先ほど同様にCognitoが用意したログインフォームがあるので、同様に作ったユーザーでログインします。
すると、以下のURLにリダイレクトされます。https://[コールバックURL]?code={認可トークン}これも、リダイレクトURLで得られた認可トークンを保存して使用していくことになります。
今は何をしていたのか
今試してみた「Cognitoが用意したUIを使う」方法は、以下のAWS Blackbelt Seminor資料における「Cognito Auth API と Hosted UI を利用」に該当します。
画像出典・資料:[AWS Black Belt Online Seminar] Amazon Cognito「ログイン・サインインフォームのUIも自分で作りたい!」という場合や、「リダイレクトURLから得たトークンや認可コードを自分で保存・管理しておくのがめんどくさい!」という場合は、もう一方の「Cognito Identity Provider API を利用」を選択することになります。
Reactでフロントエンドを作り、Cognito認証と連携させる
「Cognito Identity Provider API を利用」した方法がこちらです。
ここからは、Cognitoでユーザー認証をしたユーザーだけがアクセス・中身を見ることができるウェブページをReactで作成していきます。
Reactアプリの枠組みを作成
create-react-app
で簡単にアプリフレームが作れるので実行します。$ npx create-react-app [appname]これで、[appname]という名前のディレクトリができて、そこにReactアプリのフレームが出来上がっています。
パッケージのインストール
Cognitoでの認証・認可をコードベースで扱うためのパッケージをインストールします。
かつては"Amazon Cognito Identity SDK for JavaScript"等のSDKを使用していましたが、今は"AWS Amplify"の方のパッケージにCognito周りのパッケージが統合され、そちらがかなり便利なので今回はこれを使います。
公式Doc: Amplify Framework Documentation$ npm install aws-amplify aws-amplify-react @aws-amplify/ui-reactコード作成
まずは
index.js
を以下のように修正します。index.jsimport React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; + import { Amplify } from 'aws-amplify'; + + Amplify.configure({ + Auth: { + region: "Cognitoユーザープールを作ったリージョン", + userPoolId: "作成したユーザープールID", + userPoolWebClientId: "作成したアプリクライアントID" + } + }); ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') ); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister();
Amplify.configure()
メソッドを使って、ReactアプリでどのCognitoプールを利用するかを指定しています。
参考:公式Doc: Amplify Doc AUTHENTICATION Create or re-use existing backend次に、
App.js
を修正します。App.jsimport React from 'react'; import logo from './logo.svg'; import './App.css'; + import { withAuthenticator } from '@aws-amplify/ui-react'; function App() { // (略) } - export default App; + export default withAuthenticator(App);
withAuthenticator()
関数はhigher-order component (HoC)という、Reactコンポーネントを受け取って別のReactコンポーネントを返すものです。
withAuthenticator(引数)
で得られるコンポーネントを利用することで、引数で渡したコンポーネントの閲覧にCognitoユーザー認証を要求するようにできます。今回の場合は
App
コンポーネントを引数に渡しているので、アプリのどこの画面を見るにしてもCognito認証が必要です。
参考:公式Doc: Amplify Doc AUTHENTICATION Authenticatorアプリの起動
npm run start
でhttp://localhost:3000
が立ち上がり、アプリの挙動が確かめられるようになります。
http://localhost:3000
にブラウザでアクセスしてみると、ReactのHello, Worldは表れず、以下のような認証画面が表れます。withAuthenticator(App)
の機能通り、ここでユーザー認証が行われないと画面を閲覧することはできないようになっています。ここで、先ほどCognitoユーザープール管理画面で作ったユーザー情報を入力します。
すると、App
コンポーネントの中身である、ReactのHello, World!画面が無事に表れます。画面カスタマイズ
Sign IN/Up周りのUIをデフォルトからカスタマイズする方法を一部紹介します。
サインアウトボタンをつける
デフォルトではアプリ画面にログアウトボタンが存在しません。SAOみたいな狂気の世界にするつもりはない(はず)なので、ログアウトボタンをつけましょう。
Amplifyの
AmplifySignOut
コンポーネントが、ログアウトボタン機能を実装しているので、それを追加します。App.jsimport React from 'react'; import logo from './logo.svg'; import './App.css'; - import { withAuthenticator } from '@aws-amplify/ui-react'; + import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react'; function App() { return ( <div className="App"> <header className="App-header"> // (略) + <AmplifySignOut /> </header> </div> ); } export default withAuthenticator(App);認証フォームをカスタマイズする
デフォルトでは、Sign Upフォームは「メールアドレス」「パスワード」「電話番号」です。しかし例えば、電話番号をフォームから抜きたいというときはどうしたらいいでしょうか。
認証フォームを
withAuthenticator()
メソッドで導入した場合は、楽な代わりに細かいカスタマイズが難しいという側面があります。
実は、withAuthenticator()
というのは、AmplifyAuthenticator
コンポーネントをラップしたものです。なので、withAuthenticator(App)
とするのは以下のように書くのと同じです。App.jsimport React from 'react'; import logo from './logo.svg'; import './App.css'; - import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react'; + import { AmplifyAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react'; function App() { return ( <div className="App"> + <AmplifyAuthenticator> <header className="App-header"> // (略) <AmplifySignOut /> </header> + </AmplifyAuthenticator> </div> ); } - export default withAuthenticator(App); + export default App;このとき、
AmplifyAuthenticator
コンポーネントのブロックの中で、カスタマイズした認証フォームコンポーネントを設置すれば、デフォルト表示からフォームを変えることができます。例えば、SignUpフォームを「メールアドレス」「パスワード」だけにするときは、Sign Upフォームを作る
AmplifySignUp
コンポーネントに適切なPropsを追加してやります。App.jsimport React from 'react'; import logo from './logo.svg'; import './App.css'; - import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react'; + import { AmplifyAuthenticator, AmplifySignOut, AmplifySignUp } from '@aws-amplify/ui-react'; function App() { return ( <div className="App"> <AmplifyAuthenticator> + <AmplifySignUp + slot="sign-up" + usernameAlias="email" + formFields={[ + { + type: "email", + required: true, + }, + { + type: "password", + required: false, + }, + ]} + /> <header className="App-header"> // (略) <AmplifySignOut /> </header> </AmplifyAuthenticator> </div> ); } export default App;こうすることで、
AmplifyAuthenticator
コンポーネントのデフォルト表示が上書きされて、自分で書いたAmplifySignUp
コンポーネントが使われます。どんなコンポーネントが存在して、そんなPropsがあるのかについては、以下の公式ドキュメントを参照ください。
参考:公式Doc:Amplify Doc AUTHENTICATION Authenticator #Componentユーザープールで得られる実態を確認
- idToken: ユーザープールに格納されている情報を確認するのに必要なJWTトークン
- accessToken: ユーザープールの情報の更新のために必要なJWTトークン
- refreshToken: idTokenやaccessTokenの有効期限が切れた時に、新しいものを取得するために必要なトークン
- LastAuthUser: 現在ログインしている(=最後にログインした)ユーザーの、ユーザープール上でのID
- userData: ユーザープールに登録されている情報
参考:Cognitoのサインイン時に取得できる、IDトークン・アクセストークン・更新トークンを理解する
Amplifyのコードを使っての認証に成功した暁には、これらの重要データが自動でローカルストレージに格納されています。Cognitoが用意したUIを使用した場合とは違い、自力でのトークン保存処理の実装が必要ありません。便利です。
また、idTokenやaccessTokenの有効期限が切れた場合(デフォルトで1時間)、refreshTokenというものを使ってトークンの更新が必要なのですが、それもAmplifyを使っている場合、開発者が意識しなくても自動で更新が行われています。これもAmplifyを使用するメリットの一つです。
これらの「Amplifyが裏でうまいことやってくれている仕組み」については、以下の記事で非常に詳しく説明されていますので、気になる方はご覧ください。
参考:AmplifyでCognitoのHosted UIを利用した認証を最低限の実装で動かしてみて動作を理解するCognitoで認証した人だけが叩けるAPIを作って連携する
Cognito認証をするアプリのバックエンドで使うAPIは、認証済みの人だけが叩けるようにしないと困ります。
そのため、「Cognitoでのユーザー認証が適切になされている人のみが叩けるAPI」を作ります。Lambda関数を作る
API GatewayからGETリクエストを受け取ったら、
Secret API!
というbodyを含んだjsonを返す関数をLambdaで作ります。package main import ( "encoding/json" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) type Response struct { Body string `body` } func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { res := Response{Body: "Secret API!"} jsonBytes, _ := json.Marshal(res) return events.APIGatewayProxyResponse{ Body: string(jsonBytes), Headers: map[string]string{"Access-Control-Allow-Origin": "*"}, StatusCode: 200, }, nil } func main() { lambda.Start(handler) }コードについての解説は、以下の過去記事をご覧ください。
AWS Lambda+API Gateway+DynamoDBでCRUD APIを作るのをGolangでやってみた注:Headersの
{"Access-Control-Allow-Origin": "*"}
は、CORSを有効にするために必要になります。API Gatewayの設定
GETリクエストとLambda関数の連携
API Gatewayで、GETリクエストで上で作ったLambda関数を呼び出すように設定します(やり方は上記同様に、過去記事を参照ください)。
AWS Lambda+API Gateway+DynamoDBでCRUD APIを作るのをGolangでやってみたオーソライザの設定
ここで、APIのオーソライザの設定を行います。
以下の通りに設定して作成します。
- 名前: 任意の名前
- タイプ: Cognitoを利用
- Cognitoユーザープール: 先ほど作ったユーザープールの名前を選択
- トークンのソース: Authrizationを入力
こうすることで、HTTPリクエストのヘッダに
Authrization=IDトークン
をつけているものだけがこのAPIを叩けるようになります。
(Authrizationに有効なIDトークンが付いている=Cognitoでの認証済みとAPI Gatewayの方で判断するようになります)CORSの設定
localhost
から、別ドメイン(独自ドメインを割り当てないなら通常https://[文字列].execute-api.ap-northeast-1.amazonaws.com
となる)であるAPIを叩くので、CORS対策が必要になります。アクションから、「CROSの有効化」を選択します。
この設定のままで、CORSを有効化します。参考:【AWS】これだけ見れば理解できるCognito〜認証機能つきサーバレスアーキテクチャの作成〜
APIのデプロイ→テスト
この状態でデプロイしたAPIをターミナルからcurlで叩くと、権限なしエラーが返ってきます。
(デプロイの仕方は過去記事参照→AWS Lambda+API Gateway+DynamoDBでCRUD APIを作るのをGolangでやってみた)$ curl [APIURL] {"message":"Unauthorized"}これで、Cognitoの認証なしにはAPIが叩けなくなっているということが確認できました。
フロントエンドでAPIを呼び出す
Cognitoで認証して閲覧しているアプリから、このAPIが叩けるかどうかをテストしてみましょう。
Cognito周りのメソッドだけでなく、APIを叩くためのメソッドもAmplifyに存在するので、それを利用します。
参考:Amplify公式Doc API(REST)まずは、
index.js
内のAmplify.configure()
メソッドで、アプリ内で使うAPIを指定します。index.jsAmplify.configure({ Auth: { // (略) }, + API: { + endpoints: [ + { + name: "TestAPI", + endpoint: "APIのエンドポイントURL", + region: "APIが公開されているリージョン", + } + ] + } });次に、
App.js
を書き換えて、APIから受け取った文字列をアラートで表示するボタンを設置します。App.jsfunction App() { return ( <div className="App"> <AmplifyAuthenticator> // (略) <header className="App-header"> // (略) + <div class='container'> + <button id='button' onClick={() => showSecret()}>Click Me!</button> + </div> // (略) </header> </AmplifyAuthenticator> </div> ); }ボタンをクリックした時に実行される関数
showSecret()
も、以下のようにApp.js
内に実装します。App.jsimport { Auth, API } from 'aws-amplify'; const showSecret = async function() { const apiName = 'TestAPI'; // index.jsで指定したnameと同じ名前にする const path = ''; const myInit = { headers: { Authorization: `Bearer ${(await Auth.currentSession()).getIdToken().getJwtToken()}`, }, }; API.get(apiName, path, myInit) .then(response => { console.log(response); alert(response.Body); }) .catch(err => { console.log(err); alert(err); }); };
Authorization
ヘッダに、IDトークンを付与するように設定しています。この状態でアプリを動かして、新たに作成したボタンを押すことで、APIからのレスポンスをアラートに表示させることができます。
IDプールの作成
次に、ユーザープールに続いて、Cognitoの別機能であるIDプールを使っていきます。
なぜIDプールを作成するのか
ユーザープールだけでもログインフォームが作れたのに、どうしてIDプールも作るの?ということを説明します。
Cognitoのユーザープールで行えるのは「認証」のみで「認可」は行うことができません。
例えば、「このリクエストを送っているのは、メールアドレスhttp://example.com
で登録したAさんだな」ということを保証することはできても、「そのAさんにDBアクセスをやらせていいよ」と許可することはできないのです。なので、認証したユーザーにDynamoDB等の「AWSリソースにアクセスさせるために」「IAMロールを割り当てる」という「認可」の作業を行うためには、IDプールが必要になるのです。
(逆に言えば、IAMロールを絡ませる必要が全くないアプリケーションの場合、ユーザープールだけで事足ります。)作成手順
IDプール名に好きな名前をつけます。
認証されていないIDへのアクセス・認証フローの設定にはどちらにもチェックはつけずに、「認証プロバイダー」のタブを開きます。
ここで、先ほど作ったユーザープールIDとアプリクライアントIDを入力して、次に進みます。
ここで、認証されたユーザー・認証されなかったユーザーにどんなロールを与えるのかを設定する画面です。今回はそれぞれに対して「Auth_Role」「UnAuth_Role」を与えるように新しくロールを新規作成しました。ここまでの設定が済んだら、IDプールの作成は完了です。
作成完了したら、「IDプールの編集」タブから、プールのIDを確認して控えておきましょう。Reactアプリとの連携
IDプールを作成できたら、それを先ほどのReactアプリで使えるようにしましょう。
index.js
内のAmplify.configure()
メソッドの中身を以下のように編集します。index.jsAmplify.configure({ Auth: { region: "Cognitoユーザープールを作ったリージョン", userPoolId: "作成したユーザープールID", userPoolWebClientId: "作成したアプリクライアントID" + identityPoolId: "(先ほど控えた)IDプールのプールID", } });この状態でアプリを起動することで、ログインユーザー(ユーザープールのユーザー1人)ごとに、IDプールのIDが割り振られる&ログインユーザーが持つIAMロールが与えられるようになります。
IDプールから得られるものの実態を確認する
「IDプールのIDが与えられる」「IAMロールが与えられる」といっても、アプリの見た目上は何も変わらないので、「どこがどうなったの??」と思う方もいるでしょう。
ここからは、IDプールと連携したことで何を得られたのか、実態を確認してみましょう。まずはコンソールでものを確認
App.js
の中でcurrentCredentials()
というメソッドを実行して、結果をみてみましょう。App.js// (略) import { Auth } from 'aws-amplify'; function App() { + Auth.currentCredentials().then(console.log); // (略) } export default App;この状態でアプリを起動すると、コンソール上で
currentCredentials()
で得られたデータを確認できます。
補足:このcurrentCredentials()
メソッドは、IDプール未連携時にはエラーが返ってきます。ここで得られる「accessKeyId」と「secretAccessKey」が、IDプールから得ることができる"AWS Security Credentials"というものの実態です。
また今回の場合、Security Credentialsの中でも"AWS Temporary Security Credentials"というものが利用されます。なので「sessionToken」も重要な役割を果たします。"AWS Security Credentials"とは?
「accessKeyId」と「secretAccessKey」の組み合わせのことです。
AWSでは、正しいアクセスキーIDとシークレットアクセスキー(=正しいAWS Security Credentials)を持っていることで、特定のAWSアカウントのあるIAMロールを持ったユーザーであると判定しています。
わかりやすい例としては、普段ターミナルでAWS CLIを叩く方だと、
~/.aws/credentials
の設定ファイルに普段使用するアクセスキーIDとシークレットアクセスキーを保存しているかと思います。これは、ターミナルからコマンドを実行している人が、正当なIAM UserかどうかをAWS Security Credentialsで判断している例です。AWS CLIでのAWS Security Credentialsは、ユーザーがAWSコンソールで更新作業を行わない限りずっと有効であり、いわゆるPermanentなものです。
それに対して、今回のような「ユーザーがアプリにログインしている間だけ使える」ようなものを"AWS Temporary Security Credentials"と呼びます。一般的にPermanentな方のAWS Security CredentialsはIAM Userに、Temporaryな方のAWS Security CredentialsはIAM Roleに紐づいていることが多いです。
AWS Security Credentialsの使い方
ここからは、Security Credentialsを我々はどのように使うのかを見ていきます。
まずは、「accessKeyId」と「secretAccessKey」を使って(=Security Credentialsを使って)利用するAWSサービスというのは、たいていREST APIになっています(AWS APIという)。
AWS APIはリクエストがあるたびに、「このリクエスト主が正当な権限の持ち主かどうか?」というのをリクエスト内容から判断する必要があります。AWS APIが権限を確認できるように、ユーザーはhttpリクエストヘッダの
Authorization
フィールドに「Signature V4」という方式の署名を付与する必要があります。
その「Signature V4」署名の生成に必要なのが「accessKeyId」と「secretAccessKey」です。そして、AWS Temporary Security Credentialsを使用する場合は、「権限が期限切れじゃないかどうか」ということを判断するために「sessionID」が使われます(リクエストヘッダの
X-Amz-Security-Token
フィールドに埋め込む)。まとめ
今回の場合、IDプールから得られるSecurity Credentialsを利用することで、認証されたアプリの利用者がIAMロールを使えるようになるのです。
参考:【AWS】AWS APIの認証・認可の仕組みを理解する【Signature V4】
参考:【Amplify】APIのAuthorization方式「AWS_IAM」を理解する#1 ~解説編~【AWS】ユーザープールのグループを使ってユーザーごとに権限を分ける
IDプールと連携することで、「認証済みユーザーにはIAMロール"Auth_Role"」「認証されていないユーザーにはIAMロール"UnAuth_Role"」が渡されるように設定することができました。
しかし、認証済みユーザーに割り当てられるIAMロールが1つというのは少々窮屈です。例えば、認証済みユーザーの中でも、「一般ユーザー」と「管理者ユーザー」で違うロールを与えたいというケースはよくあるものでしょう。今回は、Cognitoユーザープールの「ユーザーグループ」という機能を用いることで、認証済みユーザーにそれぞれ適切なロールを分けて与えられるように設定し直してみようと思います。
参考:AWS CLIで動かして学ぶCognito IDプールを利用したAWSの一時クレデンシャルキー発行
ユーザーグループの作成
「ユーザーとグループ」からグループの作成を選択します。すると、以下のようなグループ作成画面が現れます。
ここで任意のグループ名をつけます。まずは管理者ユーザー用のグループを作るため、それっぽい名前をつけましょう。
IAMロールのところで、新しく「AdminAuth_Role」という名前(ポリシーはAuth_Roleと一旦同じものにしておく)を作成し、それを選択します。同様に、一般ユーザー用のグループ(IAMロールはAuth_Role)も作成しておきます。
ユーザーをグループに追加
ユーザーグループを作成したら、ユーザーをそこに所属させます。
ユーザー一覧からユーザーを選択して、「グループに追加」を選択することで可能です。
IDプールの設定
先ほど作ったIDプールの管理画面から、「IDプールの編集」を開きます。
編集画面の中から、「認証プロバイダー」というタブを開きます。
認証されたロールの選択というところで、「トークンからロールを選択する」と設定します。これで、ユーザーグループごとに設定されたロールを与えるように設定されました。
APIのアクセス許可をIAMロールで行う
せっかくユーザーごとに違うロールを与えられるようになったので、何かそれを活かした仕組みを作ってみたくなります。
ここでは、管理者ユーザーのロールを持った人にだけ叩けるAPIを作ってみようと思います。
ロールの設定
管理者ユーザーのロールポリシー(AdminAuth_Role)を以下のように編集します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "mobileanalytics:PutEvents", "cognito-sync:*", "cognito-identity:*", + "execute-api:Invoke" ], "Resource": [ "*" ] } ] }
"execute-api:Invoke"
を追加することで、API Gatewayで公開されているAPIを叩くための権限を付けています。API Gatewayでの設定
API Gatewayで、先ほど作ったAPIのGETメソッドリクエストの設定欄を開きます。
「認可」のところを「AWS_IAM」に設定します。
こうすることで、"execute-api:Invoke"
のポリシーがついたロールを持ったユーザー以外のリクエストを弾くようにできます。コードの準備
前述した通り、ユーザーがIAMロールを持っていることを伝えるためには、適切なヘッダをつけたhttpリクエストを送る必要があります。
しかし、Amplifyを使えばこの面倒なヘッダ設定を全部自動でやってもらえます。まずはコードを直します。
APIの認証が「Cognitoユーザープールでの認証の有無」ではなくなったため、Authorization
にIDトークンをつける必要は無くなりました。なので、その記述を削除します。App.js- const showSecret = async function() { + const showSecret = function() { const apiName = 'TestAPI'; // index.jsで指定したnameと同じ名前にする const path = ''; - const myInit = { - headers: { - Authorization: `Bearer ${(await Auth.currentSession()).getIdToken().getJwtToken()}`, - }, - }; // getメソッドを叩いている - API.get(apiName, path, myInit) + API.get(apiName, path) .then(response => { alert(response.data); }) .catch(err => { alert(err); }); };コード上では、AWS APIを利用するためのSignature V4署名やsessionID付与を行なっていません。
しかし、AmplifyのAPI.get()
メソッドを使うことで、それらの前処理を全てAmplifyに任せるようになっているので、上のコードで問題なく動きます。実際に、上記のコードでAPIを叩いたときのhttpリクエストをディベロッパーツールで見てみると、確かに
Authorization
ヘッダにSignature V4で生成した署名を、X-Amz-Security-Token
の部分にsessionTokenが自動で埋め込まれている様子が確認できます。
補足:Signature V4での署名を付与する際の
Authorization
ヘッダの仕様については、次のAWS公式ドキュメントを参照ください。→タスク 4: HTTP リクエストに署名を追加するまとめ
本記事の内容はこれで全てです。
Cognitoを触ることを通して、Cognitoの使い方だけでなく、一般的な・AWSでの認証認可の知識も深まります。
ここで行なったことは基本的なことばかりだと思うので、あとは公式ドキュメントを読みながら色々いじってみてください。
- 投稿日:2020-09-29T17:54:33+09:00
【React】styled-componentsを導入から適応まで
styled-componentsとは?
React
内でCSSを適用させるための一つの手段です。直感的で使いやすいので、今回は
styled-components
まとめてみます。基本的な構文
import * as React from "react"; import styled from "styled-components"; const Title = styled.h1` color: blue; `; const Header = <Title>ブログ</Title>;解説していきますね。
import styled from "styled-components";
上記で
styled-components
の機能をimport
します。const Title = styled.h1` color: blue; `;上記で、
<Title>ブログ</Title>
をスタイリングしています。下記に構文をまとめておきます。
const <要素名> = styled.<タグ名>` // ここにあてたいCSSを書く。 `;導入
npm i styled-components @types/styled-componentsnpmでインストールします。僕の環境では
typescript
を導入しているので、型定義も一緒にインストールしています。あとは、最初の方に解説したような形でスタイリングできます。
以上です。
- 投稿日:2020-09-29T02:03:56+09:00
今更ながら graphql-code-generator の便利さを痛感する その2
はじめに
今日も今日とて、フロントReact + バックRailsの...
ということで、今回は今更ながら graphql-code-generator の便利さを痛感するの続きとして、登録、削除等の
Mutation
の部分をやっていきます。※構成は前回と同じです。
とりあえずApolloの公式に沿ってやってみる
まずはTODOを追加する仕組みを作っていきます。
追加用のテキストフィールドを用意しました。
ここに値を入力して、エンターで追加処理が実行される仕組みとします。src/App.tsx+import { gql, useMutation } from "@apollo/client"; ... +const ADD_TODO = gql` + mutation addTodo($name: String!) { + addTodo(input: { name: $name }) { + todo { + id + name + } + } + } +`; const App = () => { const { loading, data } = useTodosQuery(); + const [addTodo] = useMutation(ADD_TODO); ... </p> + <input + type="text" + onKeyPress={(e) => { + if (e.key === "Enter") { + addTodo({ variables: { name: e.currentTarget.value } }); + } + }} + /> {loading ? ( ...文字を入力して、エンターで追加処理が実行できる仕組みができました。
エンター押下後、画面をリロードすると、TODOが追加されていることがわかります。
リロードしないと動きがわからないので、ついでに、追加処理に加え、入力欄のクリア、一覧情報の再取得処理をやっておきます。
src/App.tsx... - const { loading, data } = useTodosQuery(); + const { loading, data, refetch } = useTodosQuery(); - const [addTodo] = useMutation(ADD_TODO); + const [addTodo] = useMutation(ADD_TODO, { + update() { + refetch(); + }, + }); ... onKeyPress={(e) => { if (e.key === "Enter") { addTodo({ variables: { name: e.currentTarget.value } }); + e.currentTarget.value = ""; } }}特に型関連でエラーは起きませんでした。
もう少し便利になるよう作り込んでみる
何も入力せずに、登録処理を実行し、エラーが出るような仕組みを入れます。
Todo
にバリデーションエラーのフィールドを追加して、以下のような値がレスポンスに含まれるようにしました。"errors":[{"field":"name","error":"blank"}]また、処理の結果が正常に行われたかの
result
フィールド(Boolean)を追加しました。
result
がtrue
なら一覧をリロード、false
ならエラー情報をalert
で表示する仕組みを作ってみます。src/App.tsxconst ADD_TODO = gql` mutation addTodo($name: String!) { addTodo(input: { name: $name }) { todo { id name + errors { + field + error + } } + result } } `;src/App.tsxconst [addTodo] = useMutation(ADD_TODO, { - update() { - refetch(); - }, - }); + update( + _cache, + { + data: { + addTodo: { + todo: { errors }, + result, + }, + }, + } + ) { + if (result) { + refetch(); + } else { + errors.forEach(({ field, error }) => { + alert(`${field} ${error}`); + }); + } + }, + });
errors
の型が宣言されていないので、エラーが発生しました。型宣言をしてあげます。
type ValidationErrorType = { field: string; error: string; };-errors.forEach(({ field, error }) => { +errors.forEach(({ field, error }: ValidationErrorType) => { alert(`${field} ${error}`); });エラーは解消され、バリデーションエラーメッセージが
alert
で確認できるようになりました。前回同様、
graphql-code-generator
を使って、綺麗にしていこうと思います。graphql-code-generatorを使う
まずは設定から
queries
とは別にmutations
用のディレクトリを作成して、今回作成したクエリは、そちらに格納します。codegen.yml-documents: ./graphql/queries/*.graphql +documents: + - ./graphql/mutations/*.graphql + - ./graphql/queries/*.graphqlgraphql/mutations/add_todo.graphqlmutation addTodo($name: String!) { addTodo(input: { name: $name }) { todo { id name errors { field error } } result } }これで
yarn generate
を実行すると、src/types.d.ts
にTODOを追加する用のuseAddTodoMutation
が用意されました。
useAddTodoMutation
を使って追加処理を書き直してみます。-import { useTodosQuery } from "./types.d"; +import { useTodosQuery, useAddTodoMutation } from "./types.d"; +const [addTodo] = useMutation(ADD_TODO, { +const [addTodo] = useAddTodoMutation({ん?エラーが起きました。
どうやら
data
はnull
かundefined
の可能性があるそうです。それらを考慮して、少し書き直します。
const [addTodo] = useAddTodoMutation({ update(_cache, { data }) { const result = data?.addTodo?.result || false; const errors = data?.addTodo?.todo.errors || []; if (result) { refetch(); } else { errors.forEach((e) => { if (e) alert(`${e.field} ${e.error}`); }); } }, });少し手直しが必要でしたが、クエリと、型宣言の用意が不要になったので、ソース的にはスッキリしました
同様に削除処理を実装してみる
雑ですが、各TODOの隣に、削除ボタンを用意して、削除処理を実行できるようにします。
graphql/mutations/del_todo.graphqlmutation delTodo($id: ID!) { delTodo(input: { id: $id }) { todo { id } } }削除対象のTODOを特定するために、IDを取得する必要があります。
graphql/queries/todos.graphql
では、TODOのname
だけ取得しているので、id
もとるように修正します。graphql/queries/todos.graphqlquery todos { todos { + id name } }
yarn generate
を実行するとuseDelTodoMutation
関数がsrc/types.d.ts
に生まれました。この
useDelTodoMutation
を使って、削除
ボタンクリック時に削除処理が実行できるようにします。src/App.tsx-import { useTodosQuery, useAddTodoMutation } from "./types.d"; +import { useTodosQuery, useAddTodoMutation, useDelTodoMutation } from "./types.d"; +const [delTodo] = useDelTodoMutation({ + update() { + refetch(); + } +}); -{data && data.todos.map(({ name }, i) => <li key={i}>{name}</li>)} +{data && data.todos.map(({ id, name }, i) => <li key={i}>{name}<button onClick={() => delTodo({ variables: { id } })}>削除</button></li>)}お手軽に削除処理が実装できました
最後に
一つの画面、コンポーネントで、一覧取得、追加処理、削除処理を実装したので、少しボリュームの大きめなファイルとなってしまったかもしれません。
別のファイルに型定義やクエリ情報を定義して、
import
でも良いかもしれないですが、人力で且つ、複数人となると、管理が次第に煩雑になったりする可能性があると思います。これが、ある意味
graphql-code-generator
のルールに則って開発をしていると、その問題が解消されるのではないかと思いました。今回は、かなり小規模な例として、実装したので、導入にあたって障害となる箇所をあまり感じられませんでした。
LGTM なツールと思います
- 投稿日:2020-09-29T00:46:15+09:00
分かりすぎるVue.jsチートシート
自己紹介
むちゃんです。
関西を拠点に活動しているフロントエンドエンジニアです。HAL大阪の2回生です (2020.9.29現在)
イベントなど回っているので是非大阪辺りの方は会いましょう!!
1...JSフレームワークについて
「Angular」,「React」,「Vue」この3個がJS人気フレームワークです。
今回はその中のVueについて詳しく話していきたいと思います
2...Vue.jsとは?
Vue.jsはユーザーインターフェースを構築するためのJavaScriptのフレームワークです。
最近ではReact、Angular、Vue.jsがJavaScriptの人気フレームワークとして定着してきています。
その中でもVue.jsは初心者に優しく比較的に学習しやすい事もあり現在の国内での人気は頭一つ抜けている印象があります。Vue.jsの特徴
・コンポーネントの再利用性が高い
・学習コストが低く、取っ付きやすい
・拡張性が高い3...Vue.jsで抑えとくべきポイント
①Vuex
Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。
※貯蔵庫的な役割と思っていいです。詳しいのは公式=>https://vuex.vuejs.org/ja/
Vuexの流れ(図解)
①componentでdispatchする
②actionsでAPIデータのやり取りする
③それをcommitしてmutationsを呼ぶ
④mutationsでデータの値変える
⑤するとstateの値が変わるざっくりした流れですが公式の補足程度で見てもらえると嬉しいです。
⭐️おすすめ
[初めてのアウトプット]
・爆速理解!超簡単Vuex入門①
②基本的に使うオブジェクト構成
構造だけまとめているので詳しいことは公式で確認してください。
sample.jsver vm = new Vue({ el: 'app', // ①使用するデータ data: { name: 'note', price: 500 }, // ② 算出プロパティ computed: { isPrice: function () { return this.price / 2; } }, // ③ 使用するメソッド methods: { isPrice: function() { return this.price / 2; } } })data(使用するデータ)
sample.jsvar vm = new Vue ({ el: '#app', data: { //data: { プロパティ名: 値 } name: 'note', price: 500 } })samole.html<div id="app"> <p>{{ name }}</p> </div>computed (算出プロパティ)
sample.jsvar vm = new Vue ({ el: '#app', data: { name: 'note', price: 500 }, computed: { // isPriceを定義 isPrice: function () { return this.price / 2; } } })sample.html<div id="app"> <!-- 250が表示される --> <p>{{ isPrice }}円</p> </div>methods(メソッド)
sample.jsvar vm = new Vue ({ el: '#app', data: { name: 'note', price: 500 }, methods: { // メソッドisPriceを定義 isPrice: function() { return this.price / 2; } } })sample.html<div> <!-- ボタンをクリックするとisPrice関数が実行 --> <button @click="isPrice()">半額</button> <!-- 250円と表示 --> <p>{{ price }}円</p> </div>computedとmethodsの違い
結論 & 資料
この辺りをある程度理解できればアウトプットに全力を注いでいいと思います。
⭕️ アウトプットやオススメの記事一覧
・Vue.jsを100時間勉強して分かったこと
・5分でわかるVue.js基礎の基礎
・2019年版Vue.jsを使ってる人には必ず知っていてほしいVue.jsの武器
・Vue-routerを使って、SPAをシンプルにはじめてみる
・vueとexpressでSPAを作るあとがき
今回は自分なりにVueについて簡潔にまとめてみした
次回はReactについてまとめてみたいと思います。
Twitter @HomgMuchan ぜひフォロー待っています❗️