- 投稿日:2020-10-21T20:34:50+09:00
【React + TypeScript】プロジェクト開始時tsconfig.jsonに追記しておくべき設定2点
はじめに
TypeScriptでReactアプリを作成する際、TypeScript設定ファイルの「tsconfig.json」に追記しておくと良い設定について記録しておきます。
アプリはnodeインストール済みの環境で以下コマンドを発行して作成したものとします。
npx create-react-app hello-world --template typescript追記後のtsconfig.json
「"baseUrl": "src"」と「"downlevelIteration": true」を追記しています。
hello-world/tsconfig.json{ "compilerOptions": { "target": "es5", "lib": [ "dom", "dom.iterable", "esnext" ], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react", "baseUrl": "src", "downlevelIteration": true, }, "include": [ "src" ] }"baseUrl": "src"
追記しておくと、
node-modules/
にインストールされていないモジュールをインポートする時(src配下に自分で作成したモジュールなど)、src/
を起点としたパスで指定することができます。例として
src/components/pages/home.tsx
からsrc/components/atoms/button.tsx
を参照する際、この設定が無い場合相対パスをつなげて書かなければならないですがsrc/components/pages/home.tsximport * from '../../atoms/button'この設定を追記することで
src/components/pages/home.tsximport * from 'components/atoms/button'このように書くことができます。
"downlevelIteration": true
コンパイルターゲットをES5以前に設定している場合(「"target": "es5"」)でも、ES2015から導入された便利な記述をES5以下で実行できるようよしなに書き下してくれます。
この設定が無ければ新機能を使用した記述によるコンパイルエラーが発生してしまう可能性も有るので、最初から有効にしておくと良いです。
以上です。お疲れ様でした
- 投稿日:2020-10-21T19:04:22+09:00
FireBaseでの認証
FireBase 認証機能
FireBase 認証のまとめ
FireBase では、Google,Twitter,Github などの認証が簡単にできる。構築準備
FireBase の登録
FireBase の認証ファイルをどこかに作成firebaseConfig.tsimport firebase from 'firebase/app'; import "firebase/auth"; const firebaseConfig = { apiKey: "********", authDomain: "********", databaseURL: "********", projectId: "********", storageBucket: "********", messagingSenderId: "********", appId: "********", measurementId: "********" }; firebase.initializeApp(firebaseConfig); // Twitter認証 export const Twitter = new firebase.auth.TwitterAuthProvider(); // Google認証 export const Google = new firebase.auth.GoogleAuthProvider(); export default firebase;react の作成ページ例
react-firebaseui ですが、使用すると UI を簡単に書けます。login.tsximport React, { PureComponent } from "react"; import firebase, { Google } from "./firebaseConfig"; import { css } from "emotion"; import FirebaseUI from "react-firebaseui/StyledFirebaseAuth"; export default class login extends React.Component<any, any> { constructor(props: any) { super(props); this.state = { userData: {}, }; this.onChange_email = this.onChange_email.bind(this); this.onChange_password = this.onChange_password.bind(this); // css in js //お好みで } componentDidMount() { firebase.auth().onAuthStateChanged((user) => { this.setState({ ...this.state, userData: user }); }); } render() { return ( <div className={this.logincss}> <label> <input type="text" name="email" placeholder="test@gmail.com" onChange={(e) => this.onChange_email(e)} /> <br /> <input type="password" name="pass" placeholder="パスワード" onChange={(e) => this.onChange_password(e)} /> </label> <br /> <button className={this.buttoncss} key="login_do" onClick={(e) => { this.login(e); }} > ログイン </button> </div> ); } }Google 認証
Google の認証ですが下記の設定をする必要があります
const Google = new firebase.auth.GoogleAuthProvider();firebase の設定ファイルに書いてインポートしてください
※WEB の FireBase で Google 認証を有効にしていてくださいGoogle 認証_Login
react
login.tsximport firebase,{Google} From './firebaseConfig' export default class login extends React.Component<any, any> { // ~省略~ login() { try { firebase .auth() .signInWithPopup(Google) .then((result) => { this.setState({ ...this.state, userData: result }); }); } catch (error) { alert(error); } }これでリダイレクトで認証可能になるはずです。
Google 認証_logOut
ログアウト方法です。
logout(){ firebase.auth().signOut() }上記の処理でログアウトできます。
Twitter 認証
Twitter のデベロッパーアカウントの登録が必要です。
他にも Github、Facebook などでもこのようにデベロッパーアカウントの登録情報を求められます。
Twitter の認証ですが下記の設定をする必要がありますconst Twitter = new firebase.auth.GoogleAuthProvider();firebase の設定ファイルに書いてインポートしてください
※WEB の FireBase で Twitter 認証を有効にしていてくださいTwitter 認証_Login
react
login.tsximport firebase,{Twitter} From './firebaseConfig' export default class login extends React.Component<any, any> { // ~省略~ login() { try { firebase .auth() .signInWithPopup(Twitter) .then((result) => { this.setState({ ...this.state, userData: result }); }); } catch (error) { alert(error); } }これでリダイレクトで認証可能になるはずです。
Twitter 認証_logOut
ログアウト方法です。
logout(){ firebase.auth().signOut() }上記の処理でログアウトできます。
メールアドレスでの認証
メールアドレスでの認証は、上記のより少し手順が増えます
- 本当にそのメールアドレスが有効なのかどうか?
- パスワードは、6 文字以上
- 他の認証(Google,Twitter)で登録されたメールアドレスは、この方法では、登録できない(2 重登録になってしまうので)
などがあげられます。
メールアドレスでの認証_sign_in
FireBase にメールアドレスとパスワードを登録します
react// ~省略~ super(props); this.state = { email: "", password: "", userData: {}, }; } async sign_in(e: any) { e.preventDefault(); const { email, password } = this.state; try { await firebase .auth() .createUserWithEmailAndPassword(email, password) .then(console.log("signin_ok")); } catch (error) { alert(error); } } render() { // ~省略~ }FireBase を確認してみてください。メールアドレスが登録されているはずです。
この状態だとでたらめなメールアドレスでも登録できてしまいます。メールアドレスでの認証_メールアドレス確認
メールアドレスが有効かどうかを調べるために確認メールを送信します。
確認方法としてデータが json 形式で渡されるのでuser.emailVerified // 確認されてないと false の状態// メール送信してくれる firebase.auth().currentUser?.sendEmailVerification();async sign_in(e: any) { e.preventDefault(); const { email, password } = this.state; try { await firebase .auth() .createUserWithEmailAndPassword(email, password) .then(this.send_mail); } catch (error) { alert(error); } } // ======追記====== send_mail() { firebase.auth().currentUser?.sendEmailVerification(); }メールアドレスでの認証_login
ログインのやり方です。
async login(e: any) { e.preventDefault(); const { email, password } = this.state; try { await firebase .auth() .signInWithEmailAndPassword(email, password) .then((data) => data.user?.emailVerified ? "" : alert("メールアドレスの確認が済んでいません") ); } catch (error) { alert(error); } }まとめ
認証について自身の知見をまとめました。
参考ページ
https://firebase.google.com/docs/auth/web/manage-users?hl=ja
https://blog.katsubemakito.net/firebase/firebase-authentication-email-web1
- 投稿日:2020-10-21T18:27:09+09:00
Redux-Formで作成したフォームに初期値を渡す方法
Redux-Formで作ったフォームに、初期値を設定する際に手こずったため、その方法をまとめました。
はじめに
通常の
input
タグのように、value
属性を記載するだけでは初期値を表示できず、Redux-Formが用意している専用のプロパティを利用する必要があります。定数を渡す
Redux−Formを呼び出す際に、
initialValues
プロパティを設定すればOK。
Redux-Formの各インスタンス(name
属性)に対し、下記のように初期値を設定する。const formWrapped = reduxForm({ form: 'profile', initialValues: { userName: 'ほげほげ', //userNameの初期値は'ほげほげ' email: 'hoge@mail.com' //emailの初期値は'hoge@mail.com' } })(ProfileEdit); export default connect( mapStateToProps, { getUserData } )(formWrapped);変数を渡す
私の場合は、reduxストア内の値を
initialValues
に渡したかったのですが、
そのような変数をinitialValues
に渡す方法がわからず、ここで手こずりました。結論から言うと、変数を渡す場合は、
mapStateToProps
の中にinitialValues
プロパティを追加します。
その上で、reduxFormを呼び出す際、mapStateToProps
を引数として設定することで、初期値を表示できます。
ただし、このままだと初回レンダリング時の値が表示されたままとなります。
(初回レンダリング時に値がnull
だと、initialValues
の中身が空欄のままになってしまいます。)こちらはreduxFormに
enableReinitialize:true
プロパティを追加することで、
求める挙動にすることができます。const mapStateToProps = state => { return { selectedUser: state.selectedUser, //mapStateToPropsの中にinitialValuesプロパティを追加 initialValues: { userName: state.selectedUser.userName, email: state.selectedUser.email } } } const formWrapped = reduxForm( { form: 'profile', enableReinitialize: true //enableReinitializeプロパティを追加 }, mapStateToProps //mapStateToPropsを引数に設定 )(ProfileEdit); export default connect( mapStateToProps, { getUserData } )(formWrapped);参考リンク
https://github.com/redux-form/redux-form/issues/916
https://stackoverflow.com/questions/41267037/redux-form-initial-values-from
- 投稿日:2020-10-21T17:57:38+09:00
Recoil公式ドキュメント 翻訳⑩ Stateの永続性
Recoilの公式ドキュメントをgoogle翻訳するとコードまで翻訳されてしまうのが面倒なのでQiitaにまとめてみます。
追々追加していきます。
目次
- 前書き
- 基本チュートリアル
- ガイド
- 非同期データクエリ
- 非同期state・同期state
- Stateの永続性
- APIリファレンス
※全目次は一番下にあります
Stateの永続性
Recoilを使用すると、atomを使用してアプリケーションのstateを保持できます。
重要なお知らせ
このAPIは現在開発中であり、今後変更される予定です。お楽しみに...
Stateの保存
stateを保存するには、atomの変更を登録し、新しいstateを記録します。
React effectsを使用して、個々のatomを登録できます。(非同期state・同期state参照)
ただし、Recoilはhookを提供し、useRecoilTransactionObserver_UNSTABLE()
を使用してすべてのatomのstate変更を登録できるようにします。サブスクリプションコールバックは、すべてのatom stateを提供し、変更されたatomを通知します。
ここから、好みのストレージとシリアライゼーションで変更を保存できます。
以下は、JSONを使った基本的な実装の例です。function PersistenceObserver() { useRecoilTransactionObserver_UNSTABLE(({snapshot}) => { for (const modifiedAtom of snapshot.getNodes_UNSTABLE({isModified: true})) { const atomLoadable = snapshot.getLoadable(modifiedAtom); if (atomLoadable.state === 'hasValue') { Storage.setItem( modifiedAtom.key, JSON.stringify({value: atomLoadable.contents}), ); } } }); }記憶域は、ブラウザのURL履歴、LocalStorage、AsyncStorage、または任意の記憶域です。
すべてのatomを持続させたくないかもしれませんし、いくつかのatomは異なる持続性を持っているかもしれません。
atomごとのstate同期APIがまもなく登場するので、検討してみてください。Stateの復元
stateをストレージに保存したら、アプリケーションをロードするときに復元する必要があります。これは、
<RecoilRoot>
コンポーネントのinitializeState
プロパティを使用して実行できます。
initializeState
は、MutableSnapshotに初期atom値を設定するset()
メソッドを提供する関数です。この値は初期レンダリングに使用されます。useEffect()
hookでatom値を手動で設定すると、最初にデフォルト値を使用した初期レンダリングが行われ、無効になったりフリッカーが発生したりする可能性があります。次に基本的な例を示します。
const initializeState = ({set}) => { for(const [key, value] of Storage.entries()) { set(myLookupOfAtomWithKey(key), JSON.parse(value).value); } } return ( <RecoilRoot initializeState={initializeState}> <PersistenceObserver /> <SomeComponent /> </RecoilRoot> );注:
myLookupOfAtomWithKey()
は、keyに基づいて登録されたatomを検索するための擬似コードです。一部のatomは動的に、またはatom族を介して定義されるので、これは最良のアプローチではありません。atomごとの同期効果を定義する新しいAPIが近く公開される予定なので、より使いやすくなるはずです。同期中 State
また、ユーザーがURL永続性を使用してブラウザーの戻るボタンを押すなど、ストレージの非同期更新を現在のアプリケーションのstateと同期させることもできます。React effectを使用すると、これらの変更を適用し、変更されたatomの値を直接更新できます。
サンプルは近日公開予定...
下位互換性と値の検証
state記憶装置が信頼できない場合はどうすればよいでしょうか。
あるいは、使用しているatomまたはタイプを変更し、以前に永続化されたstateを操作する必要がある場合はどうすればよいでしょうか。APIが完成し次第、これを処理する方法についてのドキュメントと例を追加予定...
参考サイト
・公式ドキュメント
・フィデリ・IT用語辞典 シリアライゼーション
・IT用語辞典 BINARY フリッカ
・みらい翻訳
全目次
- 投稿日:2020-10-21T17:57:38+09:00
Recoil公式ドキュメント 翻訳⑩ ガイド-Stateの永続性
Recoilの公式ドキュメントをgoogle翻訳するとコードまで翻訳されてしまうのが面倒なのでQiitaにまとめてみます。
追々追加していきます。
目次
- 前書き ① ② ③ ④
- 基本チュートリアル ⑤ ⑥ ⑦
- ガイド
- ⑧非同期データクエリ
- ⑨非同期state・同期state
- ⑩Stateの永続性
- APIリファレンス
※全目次は一番下にあります
Stateの永続性
Recoilを使用すると、atomを使用してアプリケーションのstateを保持できます。
重要なお知らせ
このAPIは現在開発中であり、今後変更される予定です。お楽しみに...
Stateの保存
stateを保存するには、atomの変更を登録し、新しいstateを記録します。
React effectsを使用して、個々のatomを登録できます。(非同期state・同期state参照)
ただし、Recoilはhookを提供し、useRecoilTransactionObserver_UNSTABLE()
を使用してすべてのatomのstate変更を登録できるようにします。サブスクリプションコールバックは、すべてのatom stateを提供し、変更されたatomを通知します。
ここから、好みのストレージとシリアライゼーションで変更を保存できます。
以下は、JSONを使った基本的な実装の例です。function PersistenceObserver() { useRecoilTransactionObserver_UNSTABLE(({snapshot}) => { for (const modifiedAtom of snapshot.getNodes_UNSTABLE({isModified: true})) { const atomLoadable = snapshot.getLoadable(modifiedAtom); if (atomLoadable.state === 'hasValue') { Storage.setItem( modifiedAtom.key, JSON.stringify({value: atomLoadable.contents}), ); } } }); }記憶域は、ブラウザのURL履歴、LocalStorage、AsyncStorage、または任意の記憶域です。
すべてのatomを持続させたくないかもしれませんし、いくつかのatomは異なる持続性を持っているかもしれません。
atomごとのstate同期APIがまもなく登場するので、検討してみてください。Stateの復元
stateをストレージに保存したら、アプリケーションをロードするときに復元する必要があります。これは、
<RecoilRoot>
コンポーネントのinitializeState
プロパティを使用して実行できます。
initializeState
は、MutableSnapshotに初期atom値を設定するset()
メソッドを提供する関数です。この値は初期レンダリングに使用されます。useEffect()
hookでatom値を手動で設定すると、最初にデフォルト値を使用した初期レンダリングが行われ、無効になったりフリッカーが発生したりする可能性があります。次に基本的な例を示します。
const initializeState = ({set}) => { for(const [key, value] of Storage.entries()) { set(myLookupOfAtomWithKey(key), JSON.parse(value).value); } } return ( <RecoilRoot initializeState={initializeState}> <PersistenceObserver /> <SomeComponent /> </RecoilRoot> );注:
myLookupOfAtomWithKey()
は、keyに基づいて登録されたatomを検索するための擬似コードです。一部のatomは動的に、またはatom族を介して定義されるので、これは最良のアプローチではありません。atomごとの同期効果を定義する新しいAPIが近く公開される予定なので、より使いやすくなるはずです。同期中 State
また、ユーザーがURL永続性を使用してブラウザーの戻るボタンを押すなど、ストレージの非同期更新を現在のアプリケーションのstateと同期させることもできます。React effectを使用すると、これらの変更を適用し、変更されたatomの値を直接更新できます。
サンプルは近日公開予定...
下位互換性と値の検証
state記憶装置が信頼できない場合はどうすればよいでしょうか。
あるいは、使用しているatomまたはタイプを変更し、以前に永続化されたstateを操作する必要がある場合はどうすればよいでしょうか。APIが完成し次第、これを処理する方法についてのドキュメントと例を追加予定...
参考サイト
・公式ドキュメント
・フィデリ・IT用語辞典 シリアライゼーション
・IT用語辞典 BINARY フリッカ
・みらい翻訳
全目次
- 投稿日:2020-10-21T17:48:43+09:00
React開発時の汎用的なGitpod環境設定
- 開発時に、オンラインエディタであるGitpodを利用する機会があります。
- 同様の技術利用が多いため、環境テンプレートを作成する必要があります。
- 今回はReactを利用した開発においてのGitpod設定を記述します。
- ※Gitpodの概要や利用方法に関しては、こちら
前提
- Reactプロジェクトの準備
- 試用の場合、create-react-app等で準備
- ※npmプロジェクトであれば基本可
結果
- 先にGitpod設定ファイルである、
.gifpod.yml
の記述を示します。tasks: - init: npm ci command: npm start ports: - port: 3000 onOpen: open-preview github: prebuilds: addComment: true addBadge: true vscode: extensions: - dbaeumer.vscode-eslint@2.1.8:02aHhbJ0Q4aGdjHXlTdVKg== - esbenp.prettier-vscode@5.7.1:GDba64T6G+TUi1qmc6BE3A== - dsznajder.es7-react-js-snippets@3.0.0:9YEGn/57fW8lJt7cVxJQDQ==
- 以下、実際のGitpodでの開発画面。
内容
- 上記の結果は、主に以下を設定した開発環境を用意するための記述です。
- 環境構築時の自動コマンド指定
- 自動プレビュー
- 起動速度の短縮
- vscode拡張機能の自動追加
起動コマンド指定
.gitpod.yml
では、tasks
で環境構築時の自動コマンド指定が可能です。- そのため、React開発時に必要な以下を設定します。
- 依存関係インストール
init
(新規作成時のみ実行)プロパティで設定
- ※再起動やスナップショット時は行われない
- 開発サーバー起動
command
(開始時に毎回実行)プロパティで設定自動プレビュー
.gitpod.yml
では、ports
サーバー動作設定が可能です。- タブ抑止のため、
open-preview
でエディタ画面内にプレビューの表示を行います。起動速度の短縮
- オンラインエディタでは、起動速度が遅い場合があります。
- Gitpodでは、Githubアプリ連携によるプレビルド機能により大幅な解消が可能です。
- ※基本デフォルトで十分のため、githubで連携するのみで記述は必須では無い。
- ※拡張の場合のみ、prebuildsプロパティを記述
vscode拡張機能の自動追加
まとめ
- 上記のことから、オンライン開発環境における最低限の定型化と良質な開発性の重要性を実感。
参考
- 投稿日:2020-10-21T17:09:18+09:00
Recoil公式ドキュメント 翻訳⑨ 非同期state・同期state
Recoilの公式ドキュメントをgoogle翻訳するとコードまで翻訳されてしまうのが面倒なのでQiitaにまとめてみます。
追々追加していきます。
目次
※全目次は一番下にあります
非同期state・同期state
重要なお知らせ
このAPIは現在開発中であり、今後変更される予定です。お楽しみに...
Recoil atoms は局所的な応用stateを表します。
アプリケーションは、RESTful APIなどを介してリモートまたはサーバー側のstateを持っている場合もあります。
リモートstateをRecoil atomと同期することを検討してください。これにより、useRecoilState()
hookを使用してReactコンポーネントからstateに簡単にアクセスしたり、そのstateを他の派生state selectorのデータフローグラフへの入力として使用したりできます。
データベースまたはサーバーに読み取り専用データを問い合わせる場合は、非同期selectorの使用を検討してください。ローカルStateの例
この例では、フレンドステータスをローカルstateとしてのみ提供します。
const currentUserIDState = atom({ key: 'CurrentUserID', default: null, }); function CurrentUserInfo() { const [currentUserID] = useRecoilState(currentUserIDState); return <div>Current User: {currentUserID}</div>; }サーバからstateを同期
リモートstateの非同期変更を登録し、それに合わせてatom値を更新することができます。これは、通常 React
useEffect()
hookまたはその他の一般的なライブラリを使用して実行できます。function CurrentUserIDSubscription() { const setCurrentUserID = useSetRecoilState(currentUserIDState); useEffect(() => { RemoteStateAPI.subscribeToCurrentUserID(setCurrentUserID); // Specify how to cleanup after this effect return function cleanup() { RemoteServerAPI.unsubscribeFromCurrentUserID(setCurrentUserID); }; }, []); return null; } function MyApp() { return ( <RecoilRoot> <CurrentUserIDSubscription /> <CurrentUserInfo /> </RecoilRoot> ); }1つの場所で複数のatomの同期を処理する場合は、State Persistenceパターンも使用できます。
双方向同期
stateを同期して、ローカルの変更がサーバー上で更新されるようにすることもできます。
これは単純化した例です。フィードバックのループを避けるように注意してください。function CurrentUserIDSubscription() { const [currentUserID, setCurrentUserID] = useRecoilState(currentUserIDState); const knownServerCurrentUserID = useRef(currentUserID); // Subscribe server changes to update atom state useEffect(() => { function handleUserChange(id) { knownServerCurrentUserID.current = id; setCurrentUserID(id); } RemoteStateAPI.subscribeToCurrentUserID(handleUserChange); // Specify how to cleanup after this effect return function cleanup() { RemoteServerAPI.unsubscribeFromCurrentUserID(handleUserChange); }; }, [knownServerCurrentUserID]); // Subscribe atom changes to update server state useEffect(() => { if (currentUserID !== knownServerCurrentUserID.current) { knownServerCurrentID.current = currentUserID; RemoteServerAPI.updateCurrentUser(currentUserID); } }, [currentUserID, knownServerCurrentUserID.current]); return null; }パラメータを使用したstateの同期
atomFamilyヘルパーを使用して、パラメータに基づいてローカルstateを同期することもできます。
このhookの例の呼び出しごとにサブスクリプションが作成されるため、冗長な使用を避けるように注意してください。const friendStatusState = atomFamily({ key: 'Friend Status', default: 'offline', }); function useFriendStatusSubscription(id) { const setStatus = useSetRecoilState(friendStatusState(id)); useEffect(() => { RemoteStateAPI.subscribeToFriendStatus(id, setStatus); // Specify how to cleanup after this effect return function cleanup() { RemoteServerAPI.unsubscribeFromFriendStatus(id, setStatus); }; }, []); }データフローグラフ
リモートstateを表すためにatomを使用する利点は、他の派生stateの入力として使用できることです。
次の例では、現在のサーバのstateに基づいて、現在のユーザと友人のリストを表示します。サーバが現在のユーザを変更すると、リスト全体が再描画されます。友人のステータスだけを変更すると、そのリストエントリだけが再描画されます。リスト項目をクリックすると、現在のユーザがローカルに変更され、サーバのstateが更新されます。const userInfoQuery = selectorFamily({ key: 'UserInfoQuery', get: userID => async ({get}) => { const response = await myDBQuery({userID}); if (response.error) { throw response.error; } return response; }, }); const currentUserInfoQuery = selector({ key: 'CurrentUserInfoQuery', get: ({get}) => get(userInfoQuery(get(currentUserIDState)), }); const friendColorState = selectorFamily({ key: 'FriendColor', get: friendID => ({get}) => { const [status] = get(friendStatusState(friendID)); return status === 'offline' ? 'red' : 'green'; }, }); function FriendStatus({friendID}) { useFriendStatusSubscription(friendID); const [status] = useRecoilState(friendStatusState(friendID)); const [color] = useRecoilState(friendColorState(friendID)); const [friend] = useRecoilState(userInfoQuery(friendID)); return ( <div style={{color}}> Name: {friend.name} Status: {status} </div> ); } function CurrentUserInfo() { const {name, friendList} = useRecoilValue(currentUserInfoQuery); const setCurrentUserID = useSetRecoilState(currentUserIDState); return ( <div> <h1>{name}</h1> <ul> {friendList.map(friendID => <li key={friend.id} onClick={() => setCurrentUserID(friend.id)}> <React.Suspense fallback={<div>Loading...</div>}> <FriendStatus friendID={friendID} /> </React.Suspense> </li> )} </ul> </div> ); } function MyApp() { return ( <RecoilRoot> <ErrorBoundary> <React.Suspense fallback={<div>Loading...</div>}> <CurrentUserIDSubscription /> <CurrentUserInfo /> </React.Suspense> </ErrorBoundary> </RecoilRoot> ); }
参考サイト
・公式ドキュメント
・React ドキュメント useEffect()
・みらい翻訳
全目次
- 投稿日:2020-10-21T17:09:18+09:00
Recoil公式ドキュメント 翻訳⑨ ガイド-非同期state・同期state
Recoilの公式ドキュメントをgoogle翻訳するとコードまで翻訳されてしまうのが面倒なのでQiitaにまとめてみます。
追々追加していきます。
目次
- 前書き ① ② ③ ④
- 基本チュートリアル ⑤ ⑥ ⑦
- ガイド
- ⑧非同期データクエリ
- ⑨非同期state・同期state
- ⑩Stateの永続性
- APIリファレンス
※全目次は一番下にあります
非同期state・同期state
重要なお知らせ
このAPIは現在開発中であり、今後変更される予定です。お楽しみに...
Recoil atoms は局所的な応用stateを表します。
アプリケーションは、RESTful APIなどを介してリモートまたはサーバー側のstateを持っている場合もあります。
リモートstateをRecoil atomと同期することを検討してください。これにより、useRecoilState()
hookを使用してReactコンポーネントからstateに簡単にアクセスしたり、そのstateを他の派生state selectorのデータフローグラフへの入力として使用したりできます。
データベースまたはサーバーに読み取り専用データを問い合わせる場合は、非同期selectorの使用を検討してください。ローカルStateの例
この例では、フレンドステータスをローカルstateとしてのみ提供します。
const currentUserIDState = atom({ key: 'CurrentUserID', default: null, }); function CurrentUserInfo() { const [currentUserID] = useRecoilState(currentUserIDState); return <div>Current User: {currentUserID}</div>; }サーバからstateを同期
リモートstateの非同期変更を登録し、それに合わせてatom値を更新することができます。これは、通常 React
useEffect()
hookまたはその他の一般的なライブラリを使用して実行できます。function CurrentUserIDSubscription() { const setCurrentUserID = useSetRecoilState(currentUserIDState); useEffect(() => { RemoteStateAPI.subscribeToCurrentUserID(setCurrentUserID); // Specify how to cleanup after this effect return function cleanup() { RemoteServerAPI.unsubscribeFromCurrentUserID(setCurrentUserID); }; }, []); return null; } function MyApp() { return ( <RecoilRoot> <CurrentUserIDSubscription /> <CurrentUserInfo /> </RecoilRoot> ); }1つの場所で複数のatomの同期を処理する場合は、State Persistenceパターンも使用できます。
双方向同期
stateを同期して、ローカルの変更がサーバー上で更新されるようにすることもできます。
これは単純化した例です。フィードバックのループを避けるように注意してください。function CurrentUserIDSubscription() { const [currentUserID, setCurrentUserID] = useRecoilState(currentUserIDState); const knownServerCurrentUserID = useRef(currentUserID); // Subscribe server changes to update atom state useEffect(() => { function handleUserChange(id) { knownServerCurrentUserID.current = id; setCurrentUserID(id); } RemoteStateAPI.subscribeToCurrentUserID(handleUserChange); // Specify how to cleanup after this effect return function cleanup() { RemoteServerAPI.unsubscribeFromCurrentUserID(handleUserChange); }; }, [knownServerCurrentUserID]); // Subscribe atom changes to update server state useEffect(() => { if (currentUserID !== knownServerCurrentUserID.current) { knownServerCurrentID.current = currentUserID; RemoteServerAPI.updateCurrentUser(currentUserID); } }, [currentUserID, knownServerCurrentUserID.current]); return null; }パラメータを使用したstateの同期
atomFamilyヘルパーを使用して、パラメータに基づいてローカルstateを同期することもできます。
このhookの例の呼び出しごとにサブスクリプションが作成されるため、冗長な使用を避けるように注意してください。const friendStatusState = atomFamily({ key: 'Friend Status', default: 'offline', }); function useFriendStatusSubscription(id) { const setStatus = useSetRecoilState(friendStatusState(id)); useEffect(() => { RemoteStateAPI.subscribeToFriendStatus(id, setStatus); // Specify how to cleanup after this effect return function cleanup() { RemoteServerAPI.unsubscribeFromFriendStatus(id, setStatus); }; }, []); }データフローグラフ
リモートstateを表すためにatomを使用する利点は、他の派生stateの入力として使用できることです。
次の例では、現在のサーバのstateに基づいて、現在のユーザと友人のリストを表示します。サーバが現在のユーザを変更すると、リスト全体が再描画されます。友人のステータスだけを変更すると、そのリストエントリだけが再描画されます。リスト項目をクリックすると、現在のユーザがローカルに変更され、サーバのstateが更新されます。const userInfoQuery = selectorFamily({ key: 'UserInfoQuery', get: userID => async ({get}) => { const response = await myDBQuery({userID}); if (response.error) { throw response.error; } return response; }, }); const currentUserInfoQuery = selector({ key: 'CurrentUserInfoQuery', get: ({get}) => get(userInfoQuery(get(currentUserIDState)), }); const friendColorState = selectorFamily({ key: 'FriendColor', get: friendID => ({get}) => { const [status] = get(friendStatusState(friendID)); return status === 'offline' ? 'red' : 'green'; }, }); function FriendStatus({friendID}) { useFriendStatusSubscription(friendID); const [status] = useRecoilState(friendStatusState(friendID)); const [color] = useRecoilState(friendColorState(friendID)); const [friend] = useRecoilState(userInfoQuery(friendID)); return ( <div style={{color}}> Name: {friend.name} Status: {status} </div> ); } function CurrentUserInfo() { const {name, friendList} = useRecoilValue(currentUserInfoQuery); const setCurrentUserID = useSetRecoilState(currentUserIDState); return ( <div> <h1>{name}</h1> <ul> {friendList.map(friendID => <li key={friend.id} onClick={() => setCurrentUserID(friend.id)}> <React.Suspense fallback={<div>Loading...</div>}> <FriendStatus friendID={friendID} /> </React.Suspense> </li> )} </ul> </div> ); } function MyApp() { return ( <RecoilRoot> <ErrorBoundary> <React.Suspense fallback={<div>Loading...</div>}> <CurrentUserIDSubscription /> <CurrentUserInfo /> </React.Suspense> </ErrorBoundary> </RecoilRoot> ); }
参考サイト
・公式ドキュメント
・React ドキュメント useEffect()
・みらい翻訳
全目次
- 投稿日:2020-10-21T16:27:30+09:00
Recoil公式ドキュメント 翻訳⑧ 非同期データクエリ
Recoilの公式ドキュメントをgoogle翻訳するとコードまで翻訳されてしまうのが面倒なのでQiitaにまとめてみます。
追々追加していきます。
目次
- 前書き
- 基本チュートリアル
- ガイド
- 非同期データクエリ
- 非同期state・同期state
- Stateの永続性
- APIリファレンス
※全目次は一番下にあります
非同期データクエリ
Recoilは、stateと派生stateをデータフローグラフを介してReactコンポーネントにマップする方法を提供します。
非常に強力なのは、グラフ中の関数が非同期であることです。これにより、同期Reactコンポーネントのレンダリング関数で非同期関数を簡単に使用できるようになります。
Recoilを使用すると、selectorのデータフローグラフに同期関数と非同期関数をシームレスに混在させることができます。selectorのgetコールバックから値自体ではなくPromiseを値に返すだけで、インタフェースはまったく同じままです。これらは単なるselectorなので、他のselectorもデータをさらに変換するためにこれらに依存することができます。selectorは、Recoilデータフローグラフに非同期データを組み込む1つの方法として使用できます。
selectorは純粋な関数を表していることに注意してください。
与えられた入力の集合に対して、selectorは常に同じ結果を生成します(少なくともアプリケーションの存続期間中は)。selector評価は1回以上実行され、再起動され、キャッシュされる可能性があるため、これは重要です。このため、selectorは、クエリを繰り返すことで一貫したデータが得られる読み取り専用のDBクエリをモデル化するのに適しています。
ローカルとサーバーのstateを同期させる場合は、非同期状態・同期状態またはStateの永続性を参照してください。同期の例
ユーザー名を取得するための単純な同期atomとselectorを次に示します。
const currentUserIDState = atom({ key: 'CurrentUserID', default: 1, }); const currentUserNameState = selector({ key: 'CurrentUserName', get: ({get}) => { return tableOfUsers[get(currentUserIDState)].name; }, }); function CurrentUserInfo() { const userName = useRecoilValue(currentUserNameState); return <div>{userName}</div>; } function MyApp() { return ( <RecoilRoot> <CurrentUserInfo /> </RecoilRoot> ); }非同期の例
ユーザ名が何らかのデータベースに格納されている場合は、Promiseを返すか、非同期関数を使用するだけで済みます。依存関係が変更されると、selectorが再評価され、新しいクエリが実行されます。結果はキャッシュされるため、クエリは一意の入力ごとに1回だけ実行されます。
const currentUserNameQuery = selector({ key: 'CurrentUserName', get: async ({get}) => { const response = await myDBQuery({ userID: get(currentUserIDState), }); return response.name; }, }); function CurrentUserInfo() { const userName = useRecoilValue(currentUserNameQuery); return <div>{userName}</div>; }selectorのインターフェースは同じなので、このselectorを使用するコンポーネントは、それが同期atom state、派生selector state、または非同期クエリでバックされたかどうかを気にする必要はありません!
しかし、Reactのレンダリング機能は同期であるため、約束が解決する前に何をレンダリングするのだろうか?
Recoilは、React Suspenseと連動して保留中のデータを処理するように設計されている。Suspense境界でコンポーネントをラップすると、保留中のすべての子孫がキャッチされ、フォールバックUIがレンダリングされます。function MyApp() { return ( <RecoilRoot> <React.Suspense fallback={<div>Loading...</div>}> <CurrentUserInfo /> </React.Suspense> </RecoilRoot> ); }エラー処理
しかし、要求にエラーがある場合はどうなるでしょうか。
Recoilのselectorは、コンポーネントがその値を使おうとした場合に投げられるエラーを投げることができます。これはReact<ErrorBoundary>
でキャッチできます。たとえば、次のようになります。const currentUserNameQuery = selector({ key: 'CurrentUserName', get: async ({get}) => { const response = await myDBQuery({ userID: get(currentUserIDState), }); if (response.error) { throw response.error; } return response.name; }, }); function CurrentUserInfo() { const userName = useRecoilValue(currentUserNameQuery); return <div>{userName}</div>; } function MyApp() { return ( <RecoilRoot> <ErrorBoundary> <React.Suspense fallback={<div>Loading...</div>}> <CurrentUserInfo /> </React.Suspense> </ErrorBoundary> </RecoilRoot> ); }パラメータを使用したクエリ
派生stateだけでなく、パラメータに基づいてクエリを実行できるようにしたい場合があります。たとえば、コンポーネントプロパティに基づいてクエリを実行できます。これを行うには、selectorFamilyヘルパーを使用します。
const userNameQuery = selectorFamily({ key: 'UserName', get: userID => async () => { const response = await myDBQuery({userID}); if (response.error) { throw response.error; } return response.name; }, }); function UserInfo({userID}) { const userName = useRecoilValue(userNameQuery(userID)); return <div>{userName}</div>; } function MyApp() { return ( <RecoilRoot> <ErrorBoundary> <React.Suspense fallback={<div>Loading...</div>}> <UserInfo userID={1}/> <UserInfo userID={2}/> <UserInfo userID={3}/> </React.Suspense> </ErrorBoundary> </RecoilRoot> ); }データフローグラフ
クエリをselectorとしてモデリングすることによって、データ・フロー・グラフの混合state、派生state、クエリを作成できることを忘れないでください。stateが更新されると、このグラフはReactコンポーネントを自動的に更新して再レンダリングします。
次の例は、現在のユーザの名前と友人のリストを表示します。友達の名前をクリックすると、その友達が現在のユーザーになり、名前とリストが自動的に更新される。
const currentUserIDState = atom({ key: 'CurrentUserID', default: null, }); const userInfoQuery = selectorFamily({ key: 'UserInfoQuery', get: userID => async () => { const response = await myDBQuery({userID}); if (response.error) { throw response.error; } return response; }, }); const currentUserInfoQuery = selector({ key: 'CurrentUserInfoQuery', get: ({get}) => get(userInfoQuery(get(currentUserIDState))), }); const friendsInfoQuery = selector({ key: 'FriendsInfoQuery', get: ({get}) => { const {friendList} = get(currentUserInfoQuery); return friendList.map(friendID => get(userInfoQuery(friendID))); }, }); function CurrentUserInfo() { const currentUser = useRecoilValue(currentUserInfoQuery); const friends = useRecoilValue(friendsInfoQuery); const setCurrentUserID = useSetRecoilState(currentUserIDState); return ( <div> <h1>{currentUser.name}</h1> <ul> {friends.map(friend => <li key={friend.id} onClick={() => setCurrentUserID(friend.id)}> {friend.name} </li> )} </ul> </div> ); } function MyApp() { return ( <RecoilRoot> <ErrorBoundary> <React.Suspense fallback={<div>Loading...</div>}> <CurrentUserInfo /> </React.Suspense> </ErrorBoundary> </RecoilRoot> ); }同時要求
上記の例を見るとわかるように、
friendsInfoQuery
はクエリーを使用して、各友人の情報を取得します。しかし、これをループで行うことで、本質的にシリアライズされます。検索が早ければ、それでいいかもしれません。情報量が多い場合は、waitForAllなどの並行処理ヘルパーを使用して並行処理を実行できます。このヘルパーは、依存関係の配列と名前付きオブジェクトの両方を受け入れます。const friendsInfoQuery = selector({ key: 'FriendsInfoQuery', get: ({get}) => { const {friendList} = get(currentUserInfoQuery); const friends = get(waitForAll( friendList.map(friendID => userInfoQuery(friendID)) )); return friends; }, });waitForNoneを使用して、部分的なデータを含むUIへの増分更新を処理できます。
const friendsInfoQuery = selector({ key: 'FriendsInfoQuery', get: ({get}) => { const {friendList} = get(currentUserInfoQuery); const friendLoadables = get(waitForNone( friendList.map(friendID => userInfoQuery(friendID)) )); return friendLoadables .filter(({state}) => state === 'hasValue') .map(({contents}) => contents); }, });プリフェッチ*
パフォーマンス上の理由から、レンダリングの前にフェッチを開始することをお勧めします。これにより、レンダリングの開始時にクエリーを実行できます。Reactのドキュメントには、いくつかの例が記載されています。このパターンはRecoilでも有効です。
上の例を変更して、ユーザーがボタンをクリックしてユーザーを変更するとすぐに次のユーザー情報のフェッチを開始するようにします。
function CurrentUserInfo() { const currentUser = useRecoilValue(currentUserInfoQuery); const friends = useRecoilValue(friendsInfoQuery); const changeUser = useRecoilCallback(({snapshot, set}) => userID => { snapshot.getLoadable(userInfoQuery(userID)); // pre-fetch user info set(currentUserIDState, userID); // change current user to start new render }); return ( <div> <h1>{currentUser.name}</h1> <ul> {friends.map(friend => <li key={friend.id} onClick={() => changeUser(friend.id)}> {friend.name} </li> )} </ul> </div> ); }React Suspense を使わない
保留中の非同期セレクタを処理するためにReact Suspenseを使用する必要はありません。
useRecoilValueLoadable()
hookを使用して、レンダリング中のステータスを決定することもできます。function UserInfo({userID}) { const userNameLoadable = useRecoilValueLoadable(userNameQuery(userID)); switch (userNameLoadable.state) { case 'hasValue': return <div>{userNameLoadable.contents}</div>; case 'loading': return <div>Loading...</div>; case 'hasError': throw userNameLoadable.contents; } }
参考サイト
・公式ドキュメント
・IT用語辞典 シームレス
・React Suspense
・ErrorBoundary
・IT用語辞典 シリアライズ
・Reactドキュメント Start Fetching Early
・IT用語辞典 プリロード(フェッチ)
・みらい翻訳
全目次
- 投稿日:2020-10-21T16:27:30+09:00
Recoil公式ドキュメント 翻訳⑧ ガイド-非同期データクエリ
Recoilの公式ドキュメントをgoogle翻訳するとコードまで翻訳されてしまうのが面倒なのでQiitaにまとめてみます。
追々追加していきます。
目次
- 前書き ① ② ③ ④
- 基本チュートリアル ⑤ ⑥ ⑦
- ガイド
- ⑧非同期データクエリ
- ⑨非同期state・同期state
- ⑩Stateの永続性
- APIリファレンス
※全目次は一番下にあります
非同期データクエリ
Recoilは、stateと派生stateをデータフローグラフを介してReactコンポーネントにマップする方法を提供します。
非常に強力なのは、グラフ中の関数が非同期であることです。これにより、同期Reactコンポーネントのレンダリング関数で非同期関数を簡単に使用できるようになります。
Recoilを使用すると、selectorのデータフローグラフに同期関数と非同期関数をシームレスに混在させることができます。selectorのgetコールバックから値自体ではなくPromiseを値に返すだけで、インタフェースはまったく同じままです。これらは単なるselectorなので、他のselectorもデータをさらに変換するためにこれらに依存することができます。selectorは、Recoilデータフローグラフに非同期データを組み込む1つの方法として使用できます。
selectorは純粋な関数を表していることに注意してください。
与えられた入力の集合に対して、selectorは常に同じ結果を生成します(少なくともアプリケーションの存続期間中は)。selector評価は1回以上実行され、再起動され、キャッシュされる可能性があるため、これは重要です。このため、selectorは、クエリを繰り返すことで一貫したデータが得られる読み取り専用のDBクエリをモデル化するのに適しています。
ローカルとサーバーのstateを同期させる場合は、非同期状態・同期状態またはStateの永続性を参照してください。同期の例
ユーザー名を取得するための単純な同期atomとselectorを次に示します。
const currentUserIDState = atom({ key: 'CurrentUserID', default: 1, }); const currentUserNameState = selector({ key: 'CurrentUserName', get: ({get}) => { return tableOfUsers[get(currentUserIDState)].name; }, }); function CurrentUserInfo() { const userName = useRecoilValue(currentUserNameState); return <div>{userName}</div>; } function MyApp() { return ( <RecoilRoot> <CurrentUserInfo /> </RecoilRoot> ); }非同期の例
ユーザ名が何らかのデータベースに格納されている場合は、Promiseを返すか、非同期関数を使用するだけで済みます。依存関係が変更されると、selectorが再評価され、新しいクエリが実行されます。結果はキャッシュされるため、クエリは一意の入力ごとに1回だけ実行されます。
const currentUserNameQuery = selector({ key: 'CurrentUserName', get: async ({get}) => { const response = await myDBQuery({ userID: get(currentUserIDState), }); return response.name; }, }); function CurrentUserInfo() { const userName = useRecoilValue(currentUserNameQuery); return <div>{userName}</div>; }selectorのインターフェースは同じなので、このselectorを使用するコンポーネントは、それが同期atom state、派生selector state、または非同期クエリでバックされたかどうかを気にする必要はありません!
しかし、Reactのレンダリング機能は同期であるため、約束が解決する前に何をレンダリングするのだろうか?
Recoilは、React Suspenseと連動して保留中のデータを処理するように設計されている。Suspense境界でコンポーネントをラップすると、保留中のすべての子孫がキャッチされ、フォールバックUIがレンダリングされます。function MyApp() { return ( <RecoilRoot> <React.Suspense fallback={<div>Loading...</div>}> <CurrentUserInfo /> </React.Suspense> </RecoilRoot> ); }エラー処理
しかし、要求にエラーがある場合はどうなるでしょうか。
Recoilのselectorは、コンポーネントがその値を使おうとした場合に投げられるエラーを投げることができます。これはReact<ErrorBoundary>
でキャッチできます。たとえば、次のようになります。const currentUserNameQuery = selector({ key: 'CurrentUserName', get: async ({get}) => { const response = await myDBQuery({ userID: get(currentUserIDState), }); if (response.error) { throw response.error; } return response.name; }, }); function CurrentUserInfo() { const userName = useRecoilValue(currentUserNameQuery); return <div>{userName}</div>; } function MyApp() { return ( <RecoilRoot> <ErrorBoundary> <React.Suspense fallback={<div>Loading...</div>}> <CurrentUserInfo /> </React.Suspense> </ErrorBoundary> </RecoilRoot> ); }パラメータを使用したクエリ
派生stateだけでなく、パラメータに基づいてクエリを実行できるようにしたい場合があります。たとえば、コンポーネントプロパティに基づいてクエリを実行できます。これを行うには、selectorFamilyヘルパーを使用します。
const userNameQuery = selectorFamily({ key: 'UserName', get: userID => async () => { const response = await myDBQuery({userID}); if (response.error) { throw response.error; } return response.name; }, }); function UserInfo({userID}) { const userName = useRecoilValue(userNameQuery(userID)); return <div>{userName}</div>; } function MyApp() { return ( <RecoilRoot> <ErrorBoundary> <React.Suspense fallback={<div>Loading...</div>}> <UserInfo userID={1}/> <UserInfo userID={2}/> <UserInfo userID={3}/> </React.Suspense> </ErrorBoundary> </RecoilRoot> ); }データフローグラフ
クエリをselectorとしてモデリングすることによって、データ・フロー・グラフの混合state、派生state、クエリを作成できることを忘れないでください。stateが更新されると、このグラフはReactコンポーネントを自動的に更新して再レンダリングします。
次の例は、現在のユーザの名前と友人のリストを表示します。友達の名前をクリックすると、その友達が現在のユーザーになり、名前とリストが自動的に更新される。
const currentUserIDState = atom({ key: 'CurrentUserID', default: null, }); const userInfoQuery = selectorFamily({ key: 'UserInfoQuery', get: userID => async () => { const response = await myDBQuery({userID}); if (response.error) { throw response.error; } return response; }, }); const currentUserInfoQuery = selector({ key: 'CurrentUserInfoQuery', get: ({get}) => get(userInfoQuery(get(currentUserIDState))), }); const friendsInfoQuery = selector({ key: 'FriendsInfoQuery', get: ({get}) => { const {friendList} = get(currentUserInfoQuery); return friendList.map(friendID => get(userInfoQuery(friendID))); }, }); function CurrentUserInfo() { const currentUser = useRecoilValue(currentUserInfoQuery); const friends = useRecoilValue(friendsInfoQuery); const setCurrentUserID = useSetRecoilState(currentUserIDState); return ( <div> <h1>{currentUser.name}</h1> <ul> {friends.map(friend => <li key={friend.id} onClick={() => setCurrentUserID(friend.id)}> {friend.name} </li> )} </ul> </div> ); } function MyApp() { return ( <RecoilRoot> <ErrorBoundary> <React.Suspense fallback={<div>Loading...</div>}> <CurrentUserInfo /> </React.Suspense> </ErrorBoundary> </RecoilRoot> ); }同時要求
上記の例を見るとわかるように、
friendsInfoQuery
はクエリーを使用して、各友人の情報を取得します。しかし、これをループで行うことで、本質的にシリアライズされます。検索が早ければ、それでいいかもしれません。情報量が多い場合は、waitForAllなどの並行処理ヘルパーを使用して並行処理を実行できます。このヘルパーは、依存関係の配列と名前付きオブジェクトの両方を受け入れます。const friendsInfoQuery = selector({ key: 'FriendsInfoQuery', get: ({get}) => { const {friendList} = get(currentUserInfoQuery); const friends = get(waitForAll( friendList.map(friendID => userInfoQuery(friendID)) )); return friends; }, });waitForNoneを使用して、部分的なデータを含むUIへの増分更新を処理できます。
const friendsInfoQuery = selector({ key: 'FriendsInfoQuery', get: ({get}) => { const {friendList} = get(currentUserInfoQuery); const friendLoadables = get(waitForNone( friendList.map(friendID => userInfoQuery(friendID)) )); return friendLoadables .filter(({state}) => state === 'hasValue') .map(({contents}) => contents); }, });プリフェッチ*
パフォーマンス上の理由から、レンダリングの前にフェッチを開始することをお勧めします。これにより、レンダリングの開始時にクエリーを実行できます。Reactのドキュメントには、いくつかの例が記載されています。このパターンはRecoilでも有効です。
上の例を変更して、ユーザーがボタンをクリックしてユーザーを変更するとすぐに次のユーザー情報のフェッチを開始するようにします。
function CurrentUserInfo() { const currentUser = useRecoilValue(currentUserInfoQuery); const friends = useRecoilValue(friendsInfoQuery); const changeUser = useRecoilCallback(({snapshot, set}) => userID => { snapshot.getLoadable(userInfoQuery(userID)); // pre-fetch user info set(currentUserIDState, userID); // change current user to start new render }); return ( <div> <h1>{currentUser.name}</h1> <ul> {friends.map(friend => <li key={friend.id} onClick={() => changeUser(friend.id)}> {friend.name} </li> )} </ul> </div> ); }React Suspense を使わない
保留中の非同期セレクタを処理するためにReact Suspenseを使用する必要はありません。
useRecoilValueLoadable()
hookを使用して、レンダリング中のステータスを決定することもできます。function UserInfo({userID}) { const userNameLoadable = useRecoilValueLoadable(userNameQuery(userID)); switch (userNameLoadable.state) { case 'hasValue': return <div>{userNameLoadable.contents}</div>; case 'loading': return <div>Loading...</div>; case 'hasError': throw userNameLoadable.contents; } }
参考サイト
・公式ドキュメント
・IT用語辞典 シームレス
・React Suspense
・ErrorBoundary
・IT用語辞典 シリアライズ
・Reactドキュメント Start Fetching Early
・IT用語辞典 プリロード(フェッチ)
・みらい翻訳
全目次
- 投稿日:2020-10-21T12:17:09+09:00
Reactとは何か?Reactの基本的な考え方と使用するメリットについて解説
Reactとは何か?について、Reactを知らない方がざっくりと理解できるようにまとめてみました。
プログラミング初心者&初投稿なので至らない点もあると思いますが、
もし「ここが違うよ」という箇所があればご指摘いただけると助かります。Reactとは?
Reactとは、Facebook社が開発したJavaScriptのライブラリです。
Reactとよく比較されるVue.jsはフレームワークなので、
Reactも同じくフレームワークだと表現されることもありますが、厳密には異なります。ライブラリとフレームワークの違いについてはこちらの記事がわかりやすいと思ったので、
気になった方は参照してみてください。
https://www.e-loop.jp/knowledges/32/Reactを使用する目的
具体的にReactは何をするためのものなのか?というと、
Webのユーザーインターフェース(UI)を作るためものといえます。つまり、ユーザーとサービスの接点となる、
ユーザーが見たり操作したりする部分(メニューやボタンなど)を作るために使用されます。Reactの基本的な概念
Virtual DOM(仮想DOM)
Virtual DOMとは、Reactで使用する仮想のDOMのことをいいます。
そもそもDOMとは何かというと、Document Object Modelの略で、
JavaScriptなどのプログラミング言語から、HTMLにアクセスする窓口のことをいいます。
Webサイトの中身を変更したいときは、このDOMを通してHTMLにアクセスして操作を行います。通常のDOM操作では、中身が変更される度にブラウザをレンダリングするので、
変更する必要のないDOMまで再描画されてしまい、パフォーマンスが低いことが課題でした。一方Virtual DOMは、ブラウザのレンダリングとは別管理のため、
変更に必要なDOM(差分)だけを効率よく再描画でき、パフォーマンスの向上が期待できます。コンポーネント
コンポーネントとは、ReactにおいてUIを作る部品のようなものであり、
見た目と機能がセットになったものをいいます。例えばあるWebサイトでメニューがあったとしたら、
そのメニューのまとまりを一つのコンポーネントといえますし、
その中にある一つ一つのメニューのボタンもコンポーネントといえます。そして、Reactはそれぞれのコンポーネントで自分自身の状態を管理し、状態が変わる度に再描画します。
例えばチャットを例に考えると、チャットは新しいメッセージが送信されるたびに、
メッセージが下の方に追加されていきますよね。これをReactで考えると、まずチャット全体の一つのコンポーネントがあり、
そのコンポーネントはチャットの中身の状態を持っています。新しいメッセージが送信されると、チャットの中身は
今までのメッセージに新しいメッセージが加わった状態に変更されるので、
その変更された部分(差分)だけが書き換えられ、新しいメッセージが画面に反映されるというようなイメージです。JSX
JSXとは、Facebook社が開発した、HTMLのような文法で書けるJavaScriptの拡張言語です。
Reactを使う際は必ずJSXで書かなければいけないというわけではないのですが、
公式ドキュメントではJSXを使うことが推奨されており、業界標準にもなっています。例えばJSXでは、以下のように記述することができます。
const element = ( <h1 className="greeting"> Hello, world! </h1> );classの書き方などHTMLとは少し異なる部分もありますが、
基本的にHTMLの書き方に近く可読性が高いため、広く使われているということですね。Reactを使用するメリット
Reactを使用するメリットを3つ取り上げてみます。
1. 動きが速い
変更があった差分のみを再描画できるため、Webアプリケーションの動きを速くすることができます。2. 保守性が高い
コードを編集するとき、どこを編集したら他のどの部分に影響するのかがわかりやすいため、
状態管理がしやすくなります。3. 再利用性が高い
Webアプリケーションを構成する各要素を独立した部品として扱えるので、
それぞれの部品を複数の箇所で使い回すことができます。Reactの使用例
- Netflix
- Trello
- Slack
- Skype
- Airbnb
などなど、誰もが知っている大規模なアプリケーションでかなり多く使われています。
日本ではVue.jsの方が人気ですが、海外ではReactの方が人気だと言われています。最後に
Reactは海外での圧倒的な人気に比べると日本ではそこまで浸透していませんが、
これからどんどん広まっていけばいいなと思います。Reactに関するQiitaの記事もあまり多くないので、これから少しずつ書いていけたらと思います。
拙い文章でしたが、最後まで読んでいただきありがとうございました!
参考文献
React公式ドキュメント
https://ja.reactjs.org/日本一わかりやすいReact入門#1...Reactの基礎知識
https://www.youtube.com/watch?v=Otrc2zAlJyM&list=PLX8Rsrpnn3IWKz6H5ZEPWBY8AKWwb9qq7現代のフロントエンド開発でReactが使われる理由
https://www.youtube.com/watch?v=1kqu8TwZaZU
- 投稿日:2020-10-21T12:17:09+09:00
Reactとは何か?Reactの基本的な概念や使用するメリットについて解説
Reactとは何か?について、Reactを知らない方がざっくりと理解できるようにまとめてみました。
プログラミング初心者&初投稿なので至らない点もあると思いますが、
もし「ここが違うよ」という箇所があればご指摘いただけると助かります。Reactとは?
Reactとは、Facebook社が開発したJavaScriptのライブラリです。
Reactとよく比較されるVue.jsはフレームワークなので、
Reactも同じくフレームワークだと表現されることもありますが、厳密には異なります。ライブラリとフレームワークの違いについてはこちらの記事がわかりやすいと思ったので、
気になった方は参照してみてください。
https://www.e-loop.jp/knowledges/32/Reactを使用する目的
具体的にReactは何をするためのものなのか?というと、
Webのユーザーインターフェース(UI)を作るためものといえます。つまり、ユーザーとサービスの接点となる、
ユーザーが見たり操作したりする部分(メニューやボタンなど)を作るために使用されます。Reactを使用するメリット
Reactを使用するメリットを3つ取り上げてみます。
1. 動きが速い
変更があった差分のみを再描画できるため、Webアプリケーションの動きを速くすることができます。2. 保守性が高い
コードを編集するとき、どこを編集したら他のどの部分に影響するのかがわかりやすいため、
状態管理がしやすくなります。3. 再利用性が高い
Webアプリケーションを構成する各要素を独立した部品として扱えるので、
それぞれの部品を複数の箇所で使い回すことができます。Reactの基本的な概念
Virtual DOM(仮想DOM)
Virtual DOMとは、Reactで使用する仮想のDOMのことをいいます。
そもそもDOMとは何かというと、Document Object Modelの略で、
JavaScriptなどのプログラミング言語から、HTMLにアクセスする窓口のことをいいます。
Webサイトの中身を変更したいときは、このDOMを通してHTMLにアクセスして操作を行います。通常のDOM操作では、中身が変更される度にブラウザをレンダリングするので、
変更する必要のないDOMまで再描画されてしまい、パフォーマンスが低いことが課題でした。一方Virtual DOMは、ブラウザのレンダリングとは別管理のため、
変更に必要なDOM(差分)だけを効率よく再描画でき、パフォーマンスの向上が期待できます。コンポーネント
コンポーネントとは、ReactにおいてUIを作る部品のようなものであり、
見た目と機能がセットになったものをいいます。例えばあるWebサイトでメニューがあったとしたら、
そのメニューのまとまりを一つのコンポーネントといえますし、
その中にある一つ一つのメニューのボタンもコンポーネントといえます。そして、Reactはそれぞれのコンポーネントで自分自身の状態を管理し、状態が変わる度に再描画します。
例えばチャットを例に考えると、チャットは新しいメッセージが送信されるたびに、
メッセージが下の方に追加されていきますよね。これをReactで考えると、まずチャット全体の一つのコンポーネントがあり、
そのコンポーネントはチャットの中身の状態を持っています。新しいメッセージが送信されると、チャットの中身は
今までのメッセージに新しいメッセージが加わった状態に変更されるので、
その変更された部分(差分)だけが書き換えられ、新しいメッセージが画面に反映されるというようなイメージです。JSX
JSXとは、Facebook社が開発した、HTMLのような文法で書けるJavaScriptの拡張言語です。
Reactを使う際は必ずJSXで書かなければいけないというわけではないのですが、
公式ドキュメントではJSXを使うことが推奨されており、業界標準にもなっています。例えばJSXでは、以下のように記述することができます。
const element = ( <h1 className="greeting"> Hello, world! </h1> );classの書き方などHTMLとは少し異なる部分もありますが、
基本的にHTMLの書き方に近く可読性が高いため、広く使われているということですね。Reactの使用例
- Netflix
- Trello
- Slack
- Skype
- Airbnb
などなど、誰もが知っている大規模なアプリケーションでかなり多く使われています。
日本ではVue.jsの方が人気ですが、海外ではReactの方が人気だと言われています。最後に
Reactは海外での圧倒的な人気に比べると日本ではそこまで浸透していませんが、
これからどんどん広まっていけばいいなと思います。Reactに関するQiitaの記事もあまり多くないので、これから少しずつ書いていけたらと思います。
拙い文章でしたが、最後まで読んでいただきありがとうございました!
参考文献
React公式ドキュメント
https://ja.reactjs.org/日本一わかりやすいReact入門#1...Reactの基礎知識
https://www.youtube.com/watch?v=Otrc2zAlJyM&list=PLX8Rsrpnn3IWKz6H5ZEPWBY8AKWwb9qq7現代のフロントエンド開発でReactが使われる理由
https://www.youtube.com/watch?v=1kqu8TwZaZU
- 投稿日:2020-10-21T12:07:17+09:00
大学生が研究室配属選考での自己アピールのためにGitHubで製作物を公開した話
経緯
大学の情報系学科に通っている大学3年生です!
私の通っている大学・学科では、3年生の後半(10~11月)に研究室配属があります。この研究室配属では、各研究室が成績+面接等によって希望者の中から配属者を選考するのですが、面接では研究への興味・プログラミング能力・継続力・学習意欲などの自己アピールを求められます。そして、ここで重要なのは自己アピールにはGitHub等にあげた製作物も利用することができるという点です。
今回、私も研究室配属選考に備え、自己アピールに使うために過去の製作物をGitHubで公開し、「せっかく公開するんだったら記事でも書いてみようかな」と思って記事を書いて見ました。
製作物
Qiita_for_iOS
iOS開発の学習のために作成した
俺得Qiita閲覧アプリです。
- 記事の閲覧・検索やLGTM・ストック、ストック記事の確認などができます。
- Qiitaの公開APIを使用
- iOS13のキャッチアップを兼ねて作成したので、
UICollectionViewCompositionalLayout
やProperty Wrapper
などを盛り込んでいます。discord_clone_firebase
React開発の学習のために作成したチャットサービスDiscordのクローンアプリです。
デモ: https://discord-clone-36c89.web.app/
※ ユーザー名の入力が求められますが、「test」等を入力していただければ大丈夫です。
- メッセージ送信, 画像・ファイル送信, AddReactionなどができます
- バックエンドにFirebaseを利用
- React + Typescript + React Hooks
大学2年生以下の開発者様へ
製作物があると自己アピールに使える武器が手に入るので、作っておいて損はないと思いますし、研究室配属選考に限らず、その自己アピールが使えるケースも多いのではないでしょうか。
私の場合は製作物がiOS, Reactアプリケーションなので研究内容に直結しにくいですが、それでもプログラミング能力・継続力・学習意欲などのアピールになると思いますし、更に近年は機械学習・画像処理・音声認識などの比較的研究に関連しやすい分野も個人開発で手を出せるので、製作物で研究への興味や経験をアピールすることも可能だと思います。
もし、大学2年生以下で開発をしているのであれば、1つで良いのである程度形になった・公開できる製作物を作っておくと研究室配属で役に立つかもしれません!!
まとめ
(大学の先輩から聞いた話なのですが)
研究室配属選考の面接だと、「〇〇に興味があります。」 「プログラミングは得意です。」等の口頭での自己アピールのみの学生も多いらしく、その中で「〇〇に興味があります。〇〇を作りました。ソースコードはGitHubに上げてあります。」 「プログラミングは得意です。〇〇を作りました。△△にリリースしてあります。」と言った様に 製作物という証拠と一緒にアピールをすると信憑性が高く評価されやすい みたいです。
もちろん今までの成績も評価されますが、成績のみで全てを決める場合はむしろ少ない様です(?)。
やはり、自分がやってきたことのアウトプットは大事だなと思いました。今回、自分の過去のリポジトリで公開できる物を探したところ、とりあえずできそうだったのは2つでしたが、これを機にこれからは製作物をよりPublicに発信して行こうと思います。
私も研究室配属選考で勝ち残れる様に頑張ります!!!客観的評価がついているとより強いと思うので、製作物が良いと思ったらリポジトリにスターください(小声)(願望)(切実)
おまけ
GitHubのプロフィールも作成し、それっぽくしてみました!
- 投稿日:2020-10-21T11:00:03+09:00
reactでクラスを複数付与したい
sample.jsclassName={`mt-3など ${classes.付与したいクラス名}`}上記でちゃんと動いた。
以下のやり方もあるみたいだけど動かず…。
ライブラリ由来のクラス名と自分でつけたクラス名が混ざってるからかな?
別の場合で使えるかもしれないので一応記載しておく。sample.jsclassName={'クラス名' + ' ' + 'クラス名'} className={classes.クラスA + " " + classes.クラスB}
- 投稿日:2020-10-21T07:47:09+09:00
【React⑤】State
Stateとは
- コンポネント内で状態を管理する変数(Local State)
- Reactではコンポネントの内部で状態を保つことができる
- Propsは親コンポネントから値を渡されたのに対してStateはコンポネントの内部でのみ使用される
- Propsは変更不可(immutable)に対してStateは変更可能(mutable)
- StateはClass Componentで使う
propsについては以下参照
https://qiita.com/suema0331/private/ba7b0bd5521571df0c49
- Propsとはコンポネントの属性、あるデータの属性で参照できるもののこと
- {}で囲って渡していく
- 受けわたせるデータ型は文字列、数値、真偽、配列、オブジェクト、変数などなんでも可能
Stateを使う理由
- render()内部では値を変更してはいけない
→renderするとstateが変わって再レンダーされてまたstate変わって再レンダー∞??- setState()で値を変更する
- stateの変更が起こると再レンダーする
→そのためページリロードせずDOMが変わるので表示を切り替えられるStateの使い方
- Class Componentのconstructor()内で以下のように宣言
- オブジェクト型で書く
- 取得時には同じコンポネント内では
this.state.key
で取得可能- 子コンポネントとして参照したいときはpropsとして渡す
- renderのなかで
toggle = {this.togglePublished()}
のように渡すと関数が実行されてしまう!
→関数がtoggleで呼び出された時に実行されるようにする必要setStateでStateを変更する
- 関数にラップして使う
- setState()内に記述されたstateのみ変更
- 複数のstateを設定した場合でも、isPublishedのみ変更される
togglePublished = () =>{ this.setState( {isPublished: !this.state.isPublished}) };※注:下のように直接初期化したstateを変更してはいけない
状態を変え、DOMも更新したいので、setStateを使うことで状態を変えた時に自動でrenderが実行される。this.setState({count: this.state.count +1 }) //this.state = {count: this.state.count +1 } NG!!!renderの内で関数をtoggleで呼び出された時に実行されるようにする例
Article.jsx
のonClick
でprops.toggle
関数を呼び出した時に
toggle
というprops
を関数で渡す。Blog.jsximport React from 'React'; class Blog extends React.Component{ constructor(props){ super(props); this.state ={ isPublished:false, order=1 } } togglePublished = () =>{ this.setState( {isPublished: !this.state.isPublished}) }; render(){ return( <> <Article title={"React"} isPublished={this.state.isPublished} //toggleというpropsを関数で渡す必要 toggle = {() => this.togglePublished()} /> </> ) } } export default BlogArticle.jsximport React from 'React'; //公開状態のcheckbox //onClickでprops.toggle関数を呼び出す const Article = (props) =>{ return( <div> <h2>{props.title}</h2> <label htmlFor="check">公開状態: </label> <input type="checkbox" checked={props.isPublished} id="check" onClick={() => props.toggle()}/> </div> ) }; export default ArticlesetStateを使ってカウントボタン作成
this.state={count:0}
初期化時にstate宣言- handlePlusButtonメソッドの中でstateを変更
import React, {Component} from 'react'; const App = () => ( <Counter></Counter> ) class Counter extends Component { constructor(props){ //初期化処理で実行されるメソッド super(props) //親クラスで初期化処理を行う this.state={count:0} //オブジェクトがstateに設定される } handlePlusButton = () => { //handlePlusButtonメソッドの中でstateの値を+1する this.setState({count: this.state.count +1 })//状態を変更するメソッド} handleMinusButton = () => { this.setState({count: this.state.count -1 }) } render (){ //render関数を呼んでreturnのなかでcounter文字列を返す return( //returnするJSXは一個でないといけない <React.Fragment> <div>count: {this.state.count }</div> <button onClick={this.handlePlusButton}>+1</button> <button onClick={this.handleMinusButton}>-1</button> </React.Fragment> ) } } export default App;
- 投稿日:2020-10-21T01:05:46+09:00
ReactHooks useEffectを "超" 簡潔に説明
useEffectを勉強していて色々と混乱したので、超簡潔にまとめてみました。
index.jsconst App = (props) => { const [count, setCount] = useState(0); return ( <div> <p>The current count is {count}</p> <button onClick={() => setCount( count -1)}>-1</button> <button onClick={() => setCount(0)}>reset</button> <button onClick={() => setCount(count + 1 )}>+1</button> </div> ) } ReactDOM.render(<App />,document.getElementById('root');変数countには初期値で0が指定されており、-1ボタンを押すとマイナス1、+1ボタンを押すとプラス1づつ増えていく。また、resetボタンを押すと0に戻るという仕組みのカウント機能を元に説明します。
第二引数に何も書かない場合 (componentDidmount / ComponentDidUpdate)
index.jsuseEffect(() => { console.log('useEffect ran'); })つまり、「一番最初のレンダリング時」と「あらゆるコンポーネントのstateやpropsの値に変更がある時」に実行される。
第二引数に
[]
がつく場合 (componentDidMount)index.jsuseEffect(()=>{ console.log('This should only run once') }, [])「一番最初のレンダリング時」にのみ実行される
第二引数に値を指定した場合 (componentDidmount / ComponentDidUpdate)
index.jsuseEffect(() => { console.log('useEffect ran'); },[count])「一番最初のレンダリング時」と「ある特定のstateもしくはprops(ここではcount)の値が変更した時」に実行される
実行されるタイミングは「第二引数に何も書かない場合」と似ているが、具体的な値を第二引数に指定してあげることで限定できる。
- 投稿日:2020-10-21T00:40:26+09:00
Rreact学習日記① 〜 コンポーネントのライフサイクル〜
はじめに
改めて、Reactの学習を基礎から始めましたので、学んだことの振り返りとして、こまめにアウトプットしていきたいと思います。
前からいまいち分かっていなかったコンポーネントのライフサイクルについて、少し整理してみたいと思います。
正直理解は浅いので初学者の独り言程度に眺めてください
補足、修正等ございましたら教えてくださると助かります。1、 ライフサイクルとは
Reactには、コンポーネントと呼ばれる、仮想DOMを描画するためのまとまりがあります。
コンポーネントには、①生まれて ②成長して ③死ぬ という3つの流れがあります。
そしてこれら3つの時間の流れを「ライフサイクル」と呼びます。
Reactのクラスコンポーネントではこのライフサイクルを管理することができます。2、 ライフサイクルその1 Mounting
先ほど紹介したライフサイクルをもう少し詳しく説明していきます。
一つ目はMountingです。
これはコンポーネントが生まれる期間。すなわちコンポーネントが配置される期間のことを指します。ここでの主要なメソッドは、
・ constructor()
・ render()
・ componentDidMount()
があげられます。
componentDidMountはrender()の後に1度だけ呼ばれ、リスナーの設定やAPI通信の際に使われます。例えばクリックで数字を増やすカウンターを
親クラスコンポーネントにCounter、小コンポーネントにButtonというコンポーネントで作るとします。
【Counter.jsx】import React from 'react'; import Button from "./Button"; class Counter extends React.Component { constructor(props){ super(props); this.state = { count: 0 } } //ボタンがクリックされたらカウントアップする componentDidMount(){ document.getElememntById('counter').addEventLisner('click', this.countUp) } countUp = () => { this.setState({ count: this.state.count + 1 }) } render() { return ( <Button count={this.state.count} /> ) } } export default Counter【Button.jsx】
const Button = (props) => { return( <button id={"counter"}>{props.count}</button> ) } export default Buttonここでの処理の流れは以下の通りです。
①まず描画したいButtonコンポーネントの中のbuttonタグにid属性を振っておきます。
②ボタンがクリックされたことにより、render後に呼ばれているcomponentDidMountメソッドがコンポーネント内のCountUpメソッドを実行する
という流れになっています。これがコンポーネントが生まれるコンポーネント配置の段階です。3、 ライフサイクルその2 Updating
Updatingはコンポーネントが成長する。すなわち変更される期間のことを指します。
主要なメソッドは
・ render()
・ componentDidUpdate()こちらのrender()は仮想DOMを再描画することを指しています。
componentDidUpdateは再レンダー後に呼ばれます。
例えば、メッセージ送信アプリで、メッセージが新しく表示された時、その一番新しいメッセージまで自動でスクロールするような機能だったり、条件付きのイベントなどに使用します。たとえば、先ほどのカウンターが10を超えると最初からになる機能をつけるとします。
すると以下のように記述することができます。
【Counter.jsx】class Counter extends React.Component { //略 countDidUpdate() { if (this.state.count >= 11) { this.setState({ count: 0 }) } } //略 }componentDidMountは、実は常に呼び出されているんですが、条件式を追加して、countが10までしかできないようにしてあります。11回目のクリックがされた時、countが11になるのではなく、setStateによって0に塗り替えるという動きです。
4、 ライフサイクルその4 Unmounting
Unmountingはコンポーネントが死ぬ期間。すなわちコンポーネントが破棄される期間のことを指します。
主要なメソッドとして
・ componentWillUnmount()
があります。
こちらはコンポーネントが破棄される直前に実行されます。
ページを切り替えたりする際、リスナーの解除などを行い、余分なキャッシュを残さない役割があります。
サーバーへの負荷を軽くする上で重要です。5、 まとめ
- コンポーネントのライフサイクルは、クラスコンポーネントがもてる。
- ライフサイクルの流れは①Mounting ②Updating ③Unmounting