- 投稿日:2020-11-20T23:10:36+09:00
TypeError: Cannot assign to read only property 'jsx' of object '#<Object>'
【備忘録】
私自信TypeScriptに詳しくなかったのでこれでなぜ解決したかなど理由は分かりませんが、探すまで時間がかかったので、忘れないようにと残しておきます。
エラーまでにやったこと
$ npx create-react-app アプリ名 --template typescript $ cd アプリ名 $ yarn start <- ここでエラー発生エラー内容
$ yarn start yarn start yarn run v1.21.1 warning ../../../package.json: No license field $ react-scripts start アプリまでのパス/アプリ名/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js:239 appTsConfig.compilerOptions[option] = value; ^ TypeError: Cannot assign to read only property 'jsx' of object '#<Object>' at verifyTypeScriptSetup (/アプリまでのパス/アプリ名/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js:239:43) at Object.<anonymous> (/アプリまでのパス/アプリ名/node_modules/react-scripts/scripts/start.js:31:1) at Module._compile (internal/modules/cjs/loader.js:1137:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10) at Module.load (internal/modules/cjs/loader.js:985:32) at Function.Module._load (internal/modules/cjs/loader.js:878:14) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12) at internal/main/run_main_module.js:17:47 error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.解決策(参考URLも載せます)
/アプリ名/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js変更前 238: } else if (parsedCompilerOptions[option] !== valueToCheck) { 変更後 238: } else if (parsedCompilerOptions[option] !== valueToCheck && option !== "jsx") {こちらが参考URL
4.0.0 breaks with typescript (all versions) #9868他の方で違う方法で解決されている記事もありましたので共有します
https://qiita.com/makishy/items/913e20dff7caf458c965
- 投稿日:2020-11-20T22:19:35+09:00
【firebase authentication】永続化された認証情報の参照にAuth.currentUserは使ってはいけない
firebase.auth().currentUserとは
現在のユーザー認証情報を所持するプロパティです。
firebaseでは認証情報をクライアントに永続化が可能となっているので、永続化された認証情報が存在すれば
現在のユーザー認証情報
の対象となります。公式ドキュメント
リロードしようがブラウザ閉じようが認証状態は継続しているということですね、firebaseさまさまです。
本題
意気揚々Reactでこんなコードを書きます。
firebase.initializeApp({....}) const App = () => { retutn <Router> <LoginPage /> <HogePage /> <HugaPage /> <Router /> } if (firebase.auth().currentUser !== null) { console.log("前の認証情報が残っているよ!") } else { console.log("認証情報は残っていないよ!") } ReactDOM.render(<App />, document.getElementById("root"))実行してみましょう、LoginPageで何かしら認証を行いリロード!
ですが表示されるのは認証情報は残っていないよ!
無慈悲.....
調査するところ認証情報の永続化はデフォルトでsessionモードと言われるものとなっており、今回の意図通りの挙動をしてくれる様子です。公式ドキュメント
もう少し調べると、認証し→リロード→HogePageを動かしている際にはcurrentUserが参照できる
ここから推測するにcurrentUserにデータが入ってくるのはfirebaseSDKのモジュールが生成されるタイミングではないようで少しラグがあるようのでは。。。。。?
ですがcurrentUserに入ってくるタイミングなんてものはもちろん検知できません。
そこでAuth.onAuthStateChangedを発見、signin,signoutが発生した際にコールバックを処理してくれるようです。
使えそうなので書き換えてみましょう。const getStoredFirebaseAuthenticatedUser = () => new Promise<firebase.User | null>( (resolve, reject) => firebase.auth().onAuthStateChanged(resolve, reject) ); firebase.initializeApp({....}) const App = () => { return <Router> <LoginPage /> <HogePage /> <HugaPage /> <Router /> } getStoredFirebaseAuthenticatedUser().then(user => { if (firebase.auth().currentUser !== null) { console.log("前の認証情報が残っているよ!") } else { console.log("認証情報は残っていないよ!") } }) ReactDOM.render(<App />, document.getElementById("root"))同じく、LoginPageで何かしら認証を行いリロードしてみます。
少し待ち
前の認証情報が残っているよ!
勝利!
まとめ
- 永続化された認証情報の参照でcurrentUserを使うとnullになるケースがある
- たぶんfirebaseSDKの初期化が終わってからcurrentUserに入るまでに時間がかかる
- 初期化終了の検知はできないからOnChangeAuthStateでフックしてあげようね
(ならcurrentUserの定義
() => Promsie
にしろよ........)ということでした、ハマったので皆さんお気をつけください。
- 投稿日:2020-11-20T22:11:18+09:00
create-react-app(typescript)が動かんくなった
突然create-react-app (typescript)で作成したプロジェクトが動作しなくなった模様
いつものようにプロジェクトを作成
npx create-react-app myapp --template typescript作成したアプリを起動
cd myapp yarn start以下エラーが出る。なぜ?
/Users/hoge/react/myapp/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js:239 appTsConfig.compilerOptions[option] = value; ^ TypeError: Cannot assign to read only property 'jsx' of object '#<Object>' at verifyTypeScriptSetup (/Users/hoge/react/myapp/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js:239:43) at Object.<anonymous> (/Users/hoge/react/myapp/node_modules/react-scripts/scripts/start.js:31:1) at Module._compile (internal/modules/cjs/loader.js:778:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10) at Module.load (internal/modules/cjs/loader.js:653:32) at tryModuleLoad (internal/modules/cjs/loader.js:593:12) at Function.Module._load (internal/modules/cjs/loader.js:585:3) at Function.Module.runMain (internal/modules/cjs/loader.js:831:12) at startup (internal/bootstrap/node.js:283:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:623:3) error Command failed with exit code 1.解消方法
package.json変更
typescriptを^4.0.3から~4.0.3に変更
tsconfig.json変更
あとは、yarn.lock削除してからyarn startを実行
とりあえず、これで解消しました。どうしたんだ一体。。。
- 投稿日:2020-11-20T18:03:26+09:00
JSXとReact要素の定義を整理しました
JSX
とReact要素
の定義についてざっくりと整理してみました。JSXとは
JSXはReactでよく見るHTMLタグのようなもの。
<h1 className="greeting">Hello, world!</h1>React要素とは
JSXから生成されるJavaScriptのオブジェクト。
Reactはこのオブジェクトを読み取りDOMを構築する。上のJSXからはこのようなオブジェクトが生成される。
{ type: 'h1', props: { className: 'greeting', children: 'Hello, world!' } }JSXからReact要素が生成される過程
JSX
はBabelによりReact.createElement()
の呼び出しへとコンパイルされる。
React.createElement()
はオブジェクトを生成する。このオブジェクトを
React要素
と呼ぶ。
Reactはこのオブジェクトを読み取りDOMをレンダーする。このように
JSX
はコンパイル後にReact要素
と呼ばれるJavaScriptのオブジェクトになるので、
- 変数に代入したり、
- 関数の返り値にしたり、
- propsとして子コンポーネントに渡したり、
することができる。
最後に
とりあえず
JSX
とReact要素
は互いにコンパイル前後で呼び名が変わったもの、程度に理解しておけばよさそう。
- 投稿日:2020-11-20T18:03:26+09:00
JSXとReact要素について整理しました
JSX
とReact要素
の定義についてざっくりと整理してみました。JSXとは
JSXはReactでよく見るHTMLタグのようなもの。
<h1 className="greeting">Hello, world!</h1>React要素とは
JSXから生成されるJavaScriptのオブジェクト。
Reactはこのオブジェクトを読み取りDOMを構築する。上のJSXからはこのようなオブジェクトが生成される。
{ type: 'h1', props: { className: 'greeting', children: 'Hello, world!' } }JSXからReact要素が生成される過程
JSX
はBabelによりReact.createElement()
の呼び出しへとコンパイルされる。
React.createElement()
はオブジェクトを生成する。このオブジェクトを
React要素
と呼ぶ。
Reactはこのオブジェクトを読み取りDOMをレンダーする。このように
JSX
はコンパイル後にReact要素
と呼ばれるJavaScriptのオブジェクトになるので、
- 変数に代入したり、
- 関数の返り値にしたり、
- propsとして子コンポーネントに渡したり、
することができる。
最後に
とりあえず
JSX
とReact要素
は互いにコンパイル前後で呼び名が変わったもの、程度に理解しておけばよさそう。
- 投稿日:2020-11-20T11:48:22+09:00
react-hook-formの基本
久しぶりの投稿になります。今後は週一記事以上は書けるように頑張ります!今回はreact-hook-formについての記事です。公式ドキュメントの内容を、自分なりにまとめてみました。より詳しく知りたい方は公式ドキュメントも見てみて下さい!
参考:公式ドキュメント
react-hook-formとは
inputとかのformに関係するデータを使う時に、Stateを使わなくて良くて、レンダリング回数を減らせる!
インストール
## yarnの場合 yarn add react-hook-form ## npmの場合 npm install react-hook-form以上!TypeScriptの型定義も含まれてます。
↓↓↓簡単な例
import React from 'react'; import { useForm } from 'react-hook-form'; import { ErrorMessage } from '@hookform/error-message'; type FormInputs = { name: string; email: string; }; export const Demo = () => { const { register, errors, handleSubmit, reset } = useForm<FormInputs>(); const on_submit = (data: FormInputs) => { console.log(data); reset(); }; return ( <form onSubmit={handleSubmit(on_submit)}> <input name="name" ref={register} /> <input name="email" ref={register({ required: true, maxLength: 60, pattern: { value: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/, message: 'メールアドレスの形式が不正です', }, })} /> <button type="submit">Submit</button> <ErrorMessage errors={errors} name="email" message={errors.email?.message} /> </form> ); };↓この例だと入力した値はこんな風に出力される。それぞれの機能を解説して行きます。
{name: 入力した値,email: 入力した値}useForm
react-hook-formからインポートするものはいくつかあるが、ほとんどuseFormから取得することが多い。
const { register, reset, handleSubmit } = useForm({ mode: onSubmit, defaultValues: {name: "aaa", email: test@test.com} })引数にオブジェクトで色んな設定みたいなのができる
mode 初回のバリデーションの実行タイミング
(初期値:onSubmit)onChange or onBlur or onSubmit or onTouched or all reValidateMode 初回のバリデーション実行後、次の実行タイミング
(初期値: onChange)onChange or onBlur or onSubmit defaultValues 初期値をオブジェクトで入力します
(resetでdefaultValuesの値に戻る){name属性の値: 初期値,...} resolver 他のvalidationライブラリを使う時 shouldUnregister 入力要素がアンマウントされたら、値の参照を解除、falseなら入力された値を保持
(初期値: true)true or false register
inputなどに入力された値を参照するために使う。name属性を設定する必要があり、registerはrefに入れる。UIライブラリを使う場合は
control
参照。一番使う。↓nameの付け方によって、dataの形が変わる.
name="firstName" →{ firstName: 'value'}
name="firstName[0]" →{ firstName: [ 'value' ] }
name="name.firstName" →{ name: { firstName: 'value' } }
name="name.firstName[0]" →{ name: { firstName: [ 'value' ] } }引数にオブジェクトで色んなバリデーションが設定できる.
required 必須項目にするか true or false or エラーメッセージ maxLength 最大文字数 {value: number, message: エラーメッセージ} minLenght 最小文字数 {value: number, message: エラーメッセージ} max 最大データ量 {value: number, message: エラーメッセージ} min 最小データ量 {value: number, message: エラーメッセージ} pattern 文字の形式 {value: 正規表現, message: エラーメッセージ} validate カスタム {任意の名前: value => booleanを返す式 or 関数 || エラーメッセージ} <input name="email" ref={register({ required: true, maxLength: 60, pattern: { value: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/, message: 'メールアドレスの形式が不正です', }, })} validate: { lessThanTen: value => parseInt(value, 60) < 60 || '60文字以内で入力して下さい' } />unregister
引数に入れたnameはregisterで参照しなくなる。配列を入れて、複数も可能。
unregister(['name','email'])
<button onClick={() => unregister("email")}>email_unregister</button>errors
registerなどで設定したバリデーションを通らなかった時にエラーのデータが入る。エラーメッセージを設定している場合は、それも取得できる。
<input name="name" ref={register({required: '入力して下さい'}) /> {errors.name && <p>errors.name.message</p>}watch
第一引数入れたnameの値を監視。第二引数は初期値を設定可能。unregisterと同様に配列に入れて複数も可能。何も入れないと、全てのnameの値を取得。レンダリング回数は増える。
const watch_name = watch("name", false); const watch_all_fields = watch(); const watch_fields = watch(["name", "email"]);handleSubmit
ラップした関数にformのdataをオブジェクトの形で渡してくれる。
on_submit
の引数にdataがあるのはそのため。
event.preventDefalut()
も必要なし。const on_submit = (data: FormInputs) => { console.log(data); }; return ( <form onSubmit={handleSubmit(on_submit)}> ...reset
formの中の状態を初期化する関数。
defaultValue
が設定されていたら、その値になるconst on_click =()=>{ reset(); }setError
第一引数に指定したnameにエラーをセットする関数。第二引数にtype(requiredやminLengthなど)とエラーメッセージをオブジェクトとしていれる。
typeを変えるとどう変わるかが、よく分からないので分かる方いたら教えて下さい!<button onClick={() => setError("email", { type: "required", message: "" })}>clearErrors
引数に入れたnameのerrorを消す関数。こちらも配列で複数指定できる。
<button onClick={() => clearErrors("name")}>getValues
引数に入れたnameの現在の値を取得。こちらも配列に入れて複数、空なら全てのnameの値を取得。watchとの違いは常に監視せず、再レンダリングもされない。ボタンを押して時に取得するとかならこっちの方がパフォーマンス的に良さそう。
const values = getValues(); const single_value = getValues("name"); const multiple_values = getValues(["name", "email"]);setValue
第一引数に入れたnameに第二引数に入れた値をセットする関数。その際に
shouldValidate
でバリデーションを実行するかどうか、shouldDirty
で変更後に変更の有無の判定を行うかどうか<button onClick={()=> setValue('name', 'value', { shouldValidate: true, shouldDirty: true })}>trigger
引数に入れたnameのバリデーションを実行する関数。こちらも配列で複数、空なら全体。
<button type="button" onClick={() => { trigger("lastName") }}>Trigger</button>control
Controller
のnameの値を参照するのに使う。バリデーションはできない。useWatch
、useFieldArray
にも使う。<Controller as={<TextInput />} control={control} name="name" />formState
form内の入力の有無や送信の状態などを取得できる。
formState.isSubmitting
はbuttonにローディングをつける時によく使う。
isDirty 全体で何かしら変更があったらtrueになる true or false dirtyFields それぞれの入力要素で何かしらの変更があったらtrue {name属性の値: true or false,...} touched それぞれの入力要素で何かしらの操作をしたらtrueになる
(フォーカスするだけでも、onBlurのタイミングで反映される){name属性の値: true or false,...} isSubmitted formが送信されたらtrue、resetでfalseに戻る true or false isSubmitSuccessful formの送信が成功したらtrue true or false isSubmitting formの送信中にtrue true or false submitCount formの送信された回数 number isValid 何かしらエラーがあったらtrue
(modeがonChange or onBlurの時のみ)true or false errors バリデーションのエラーのオブジェクトが入る {name属性の値: {
type: "required",
message: "",
ref: <input name="name属性の値" type="text">,
},...}import React from "react"; import { useForm } from "react-hook-form"; export const Demo = () => { const { register, handleSubmit, errors, formState } = useForm(); const on_submit = data => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="name" ref={register} /> {isDirty && <p>Form is dirty.</p>} {isSubmitting && <span>Submitting...</span>} <button type="submit" /> </form> ); }Controller
UIライブラリを使う時に使用。
register
だと参照できないことが多い。(MaterialUIのTextField
とかはinputRef
にregister
を入れて参照できる)。rule
でregister
の中に書く感じでバリデーションを書ける。defaultValue
も設定可能
asの中に全部入れちゃうのが簡単<Controller as={<TextInput />} control={control} name="name" rules={{required: true}} defaultValue="aaa" /> <Controller control={control} name="email" render={({ onChange, onBlur, value, name, ref }) => ( <TextField onBlur={onBlur} onChange={onChange} checked={value} inputRef={ref} /> )} />ErrorMessage
指定した
name
のエラーメッセージを表示できる。message
はstringならOK<ErrorMessage errors={errors} name="email" message={errors.email?.message} />useFormContext,FormProvider
FormProvider
にuseForm
のregister
やerrors
などをまとめて渡して、ラップされたコンポーネントでuseFormContext
を使うと、その渡された値を使うことができる。import React from "react"; import { useForm, FormProvider, useFormContext } from "react-hook-form"; export const Demo = () => { const methods = useForm(); const on_submit = data => console.log(data); return ( <FormProvider {...methods} > <form onSubmit={methods.handleSubmit(onSubmit)}> <Input /> <button type="submit" /> </form> </FormProvider> ); } const Input = () => { const { register } = useFormContext(); return <input name="name" ref={register} />; }useWatch
controlを渡すことで
watch
を別のコンポーネントでも使えるようにした感じ。import React from "react"; import { useForm, useWatch } from "react-hook-form"; const Watch = ({ control }) => { const name = useWatch({ control, name: 'name', defaultValue: 'aaa' }); return <div>{name}</div>; } type FieldInputs = { name: string; } export const Demo = () => { const { register, control, handleSubmit } = useForm<FieldInputs>(); return ( <form onSubmit={handleSubmit(data => console.log("data", data))}> <input ref={register} name="name" /> <Watch control={control} /> <button type="submit" /> </form> ); }useFieldArray
下の写真みたいな感じで、同じinputなどを増減させたい時に使えます。公式ドキュメントから、YoutubeのuseFieldArryの紹介動画に飛べるので見るとわかりやすいです。
↓こんな感じでuseFormのように色んなメソッドを使えます。nameとcontrolが必要です。defaultValueはuseFormで設定できます。
const { fields, append, prepend, remove, swap, move, insert } = useFieldArray({ control, name: "demo" });↓3列目は例です。
fields mapを使ったりして、入力要素をレンダリングするのに使用。idやdefaultValueが入ったオブジェクト append fieldsの最後にinputを追加 append({name: "aaa"}) prepend fieldsの先頭にinputを追加 prepend({name: "aaa"}) insert fieldsの特定の位置にinputを追加 insert(3,{name: "aaa"}) swap inputの位置を入れ替える swap(1,2) move inputの位置を動かす move(1,2) remove 特定のinputを削除、引数無しで全削除 remove(1) remove() import React from 'react'; import { useForm, useFieldArray } from 'react-hook-form'; export const Demo = () => { const { register, control, handleSubmit } = useForm({ defaultValues: { demo: [{ name: 'name', email: 'email@email.com' }], }, }); const { fields, append, prepend, remove } = useFieldArray({ control, name: 'demo', }); return ( <form onSubmit={handleSubmit((data) => console.log(data))}> {fields.map((item, index) => ( <div key={item.id}> <input name={`demo[${index}].name`} ref={register} defaultValue={item.name} /> <input name={`demo[${index}].email`} ref={register} defaultValue={item.email} /> <button type="button" onClick={() => remove(index)}> Delete </button> </div> ))} <button type="button" onClick={() => append({ name: 'appendName', email: 'append@email.com' }) } > append </button> <button type="button" onClick={() => prepend({ name: 'prependName', email: 'prepend@email.com', }) } > prepend </button> <input type="submit" /> </form> ); };終わりに
ここまで読んでいただきありがとうございます!
少しでもreact-hook-formの理解が深まれば幸いです。自分も書きながら、react-hook-formの便利さが機能が理解できました。
質問、感想、改善点などをコメントでいただけるとモチベーションにつながりますのでよければお願いします!
- 投稿日:2020-11-20T11:48:22+09:00
react-hook-formの使い方を解説!
久しぶりの投稿になります。今後は週一記事以上は書けるように頑張ります!今回はreact-hook-formについての記事です。公式ドキュメントの内容を、自分なりにまとめてみました。より詳しく知りたい方は公式ドキュメントも見てみて下さい!
参考:公式ドキュメント
react-hook-formとは
inputとかのformに関係するデータを使う時に、Stateを使わなくて良くて、レンダリング回数を減らせる!
インストール
## yarnの場合 yarn add react-hook-form ## npmの場合 npm install react-hook-form以上!TypeScriptの型定義も含まれてます。
↓↓↓簡単な例
import React from 'react'; import { useForm } from 'react-hook-form'; import { ErrorMessage } from '@hookform/error-message'; type FormInputs = { name: string; email: string; }; export const Demo = () => { const { register, errors, handleSubmit, reset } = useForm<FormInputs>(); const on_submit = (data: FormInputs) => { console.log(data); reset(); }; return ( <form onSubmit={handleSubmit(on_submit)}> <input name="name" ref={register} /> <input name="email" ref={register({ required: true, maxLength: 60, pattern: { value: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/, message: 'メールアドレスの形式が不正です', }, })} /> <button type="submit">Submit</button> <ErrorMessage errors={errors} name="email" message={errors.email?.message} /> </form> ); };↓この例だと入力した値はこんな風に出力される。それぞれの機能を解説して行きます。
{name: 入力した値,email: 入力した値}useForm
react-hook-formからインポートするものはいくつかあるが、ほとんどuseFormから取得することが多い。
const { register, reset, handleSubmit } = useForm({ mode: onSubmit, defaultValues: {name: "aaa", email: test@test.com} })引数にオブジェクトで色んな設定みたいなのができる
mode 初回のバリデーションの実行タイミング
(初期値:onSubmit)onChange or onBlur or onSubmit or onTouched or all reValidateMode 初回のバリデーション実行後、次の実行タイミング
(初期値: onChange)onChange or onBlur or onSubmit defaultValues 初期値をオブジェクトで入力します
(resetでdefaultValuesの値に戻る){name属性の値: 初期値,...} resolver 他のvalidationライブラリを使う時 shouldUnregister 入力要素がアンマウントされたら、値の参照を解除、falseなら入力された値を保持
(初期値: true)true or false register
inputなどに入力された値を参照するために使う。name属性を設定する必要があり、registerはrefに入れる。UIライブラリを使う場合は
control
参照。一番使う。↓nameの付け方によって、dataの形が変わる.
name="firstName" →{ firstName: 'value'}
name="firstName[0]" →{ firstName: [ 'value' ] }
name="name.firstName" →{ name: { firstName: 'value' } }
name="name.firstName[0]" →{ name: { firstName: [ 'value' ] } }引数にオブジェクトで色んなバリデーションが設定できる.
required 必須項目にするか true or false or エラーメッセージ maxLength 最大文字数 {value: number, message: エラーメッセージ} minLenght 最小文字数 {value: number, message: エラーメッセージ} max 最大データ量 {value: number, message: エラーメッセージ} min 最小データ量 {value: number, message: エラーメッセージ} pattern 文字の形式 {value: 正規表現, message: エラーメッセージ} validate カスタム {任意の名前: value => booleanを返す式 or 関数 || エラーメッセージ} <input name="email" ref={register({ required: true, maxLength: 60, pattern: { value: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/, message: 'メールアドレスの形式が不正です', }, })} validate: { lessThanTen: value => parseInt(value, 60) < 60 || '60文字以内で入力して下さい' } />unregister
引数に入れたnameはregisterで参照しなくなる。配列を入れて、複数も可能。
unregister(['name','email'])
<button onClick={() => unregister("email")}>email_unregister</button>errors
registerなどで設定したバリデーションを通らなかった時にエラーのデータが入る。エラーメッセージを設定している場合は、それも取得できる。
<input name="name" ref={register({required: '入力して下さい'}) /> {errors.name && <p>errors.name.message</p>}watch
第一引数入れたnameの値を監視。第二引数は初期値を設定可能。unregisterと同様に配列に入れて複数も可能。何も入れないと、全てのnameの値を取得。レンダリング回数は増える。
const watch_name = watch("name", false); const watch_all_fields = watch(); const watch_fields = watch(["name", "email"]);handleSubmit
ラップした関数にformのdataをオブジェクトの形で渡してくれる。
on_submit
の引数にdataがあるのはそのため。
event.preventDefalut()
も必要なし。const on_submit = (data: FormInputs) => { console.log(data); }; return ( <form onSubmit={handleSubmit(on_submit)}> ...reset
formの中の状態を初期化する関数。
defaultValue
が設定されていたら、その値になるconst on_click =()=>{ reset(); }setError
第一引数に指定したnameにエラーをセットする関数。第二引数にtype(requiredやminLengthなど)とエラーメッセージをオブジェクトとしていれる。
typeを変えるとどう変わるかが、よく分からないので分かる方いたら教えて下さい!<button onClick={() => setError("email", { type: "required", message: "" })}>clearErrors
引数に入れたnameのerrorを消す関数。こちらも配列で複数指定できる。
<button onClick={() => clearErrors("name")}>getValues
引数に入れたnameの現在の値を取得。こちらも配列に入れて複数、空なら全てのnameの値を取得。watchとの違いは常に監視せず、再レンダリングもされない。ボタンを押して時に取得するとかならこっちの方がパフォーマンス的に良さそう。
const values = getValues(); const single_value = getValues("name"); const multiple_values = getValues(["name", "email"]);setValue
第一引数に入れたnameに第二引数に入れた値をセットする関数。その際に
shouldValidate
でバリデーションを実行するかどうか、shouldDirty
で変更後に変更の有無の判定を行うかどうか<button onClick={()=> setValue('name', 'value', { shouldValidate: true, shouldDirty: true })}>trigger
引数に入れたnameのバリデーションを実行する関数。こちらも配列で複数、空なら全体。
<button type="button" onClick={() => { trigger("lastName") }}>Trigger</button>control
Controller
のnameの値を参照するのに使う。バリデーションはできない。useWatch
、useFieldArray
にも使う。<Controller as={<TextInput />} control={control} name="name" />formState
form内の入力の有無や送信の状態などを取得できる。
formState.isSubmitting
はbuttonにローディングをつける時によく使う。
isDirty 全体で何かしら変更があったらtrueになる true or false dirtyFields それぞれの入力要素で何かしらの変更があったらtrue {name属性の値: true or false,...} touched それぞれの入力要素で何かしらの操作をしたらtrueになる
(フォーカスするだけでも、onBlurのタイミングで反映される){name属性の値: true or false,...} isSubmitted formが送信されたらtrue、resetでfalseに戻る true or false isSubmitSuccessful formの送信が成功したらtrue true or false isSubmitting formの送信中にtrue true or false submitCount formの送信された回数 number isValid 何かしらエラーがあったらtrue
(modeがonChange or onBlurの時のみ)true or false errors バリデーションのエラーのオブジェクトが入る {name属性の値: {
type: "required",
message: "",
ref: <input name="name属性の値" type="text">,
},...}import React from "react"; import { useForm } from "react-hook-form"; export const Demo = () => { const { register, handleSubmit, errors, formState } = useForm(); const on_submit = data => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="name" ref={register} /> {isDirty && <p>Form is dirty.</p>} {isSubmitting && <span>Submitting...</span>} <button type="submit" /> </form> ); }Controller
UIライブラリを使う時に使用。
register
だと参照できないことが多い。(MaterialUIのTextField
とかはinputRef
にregister
を入れて参照できる)。rule
でregister
の中に書く感じでバリデーションを書ける。defaultValue
も設定可能
asの中に全部入れちゃうのが簡単<Controller as={<TextInput />} control={control} name="name" rules={{required: true}} defaultValue="aaa" /> <Controller control={control} name="email" render={({ onChange, onBlur, value, name, ref }) => ( <TextField onBlur={onBlur} onChange={onChange} checked={value} inputRef={ref} /> )} />ErrorMessage
指定した
name
のエラーメッセージを表示できる。message
はstringならOK<ErrorMessage errors={errors} name="email" message={errors.email?.message} />useFormContext,FormProvider
FormProvider
にuseForm
のregister
やerrors
などをまとめて渡して、ラップされたコンポーネントでuseFormContext
を使うと、その渡された値を使うことができる。import React from "react"; import { useForm, FormProvider, useFormContext } from "react-hook-form"; export const Demo = () => { const methods = useForm(); const on_submit = data => console.log(data); return ( <FormProvider {...methods} > <form onSubmit={methods.handleSubmit(onSubmit)}> <Input /> <button type="submit" /> </form> </FormProvider> ); } const Input = () => { const { register } = useFormContext(); return <input name="name" ref={register} />; }useWatch
controlを渡すことで
watch
を別のコンポーネントでも使えるようにした感じ。import React from "react"; import { useForm, useWatch } from "react-hook-form"; const Watch = ({ control }) => { const name = useWatch({ control, name: 'name', defaultValue: 'aaa' }); return <div>{name}</div>; } type FieldInputs = { name: string; } export const Demo = () => { const { register, control, handleSubmit } = useForm<FieldInputs>(); return ( <form onSubmit={handleSubmit(data => console.log("data", data))}> <input ref={register} name="name" /> <Watch control={control} /> <button type="submit" /> </form> ); }useFieldArray
下の写真みたいな感じで、同じinputなどを増減させたい時に使えます。公式ドキュメントから、YoutubeのuseFieldArryの紹介動画に飛べるので見るとわかりやすいです。
↓こんな感じでuseFormのように色んなメソッドを使えます。nameとcontrolが必要です。defaultValueはuseFormで設定できます。
const { fields, append, prepend, remove, swap, move, insert } = useFieldArray({ control, name: "demo" });↓3列目は例です。
fields mapを使ったりして、入力要素をレンダリングするのに使用。idやdefaultValueが入ったオブジェクト append fieldsの最後にinputを追加 append({name: "aaa"}) prepend fieldsの先頭にinputを追加 prepend({name: "aaa"}) insert fieldsの特定の位置にinputを追加 insert(3,{name: "aaa"}) swap inputの位置を入れ替える swap(1,2) move inputの位置を動かす move(1,2) remove 特定のinputを削除、引数無しで全削除 remove(1) remove() import React from 'react'; import { useForm, useFieldArray } from 'react-hook-form'; export const Demo = () => { const { register, control, handleSubmit } = useForm({ defaultValues: { demo: [{ name: 'name', email: 'email@email.com' }], }, }); const { fields, append, prepend, remove } = useFieldArray({ control, name: 'demo', }); return ( <form onSubmit={handleSubmit((data) => console.log(data))}> {fields.map((item, index) => ( <div key={item.id}> <input name={`demo[${index}].name`} ref={register} defaultValue={item.name} /> <input name={`demo[${index}].email`} ref={register} defaultValue={item.email} /> <button type="button" onClick={() => remove(index)}> Delete </button> </div> ))} <button type="button" onClick={() => append({ name: 'appendName', email: 'append@email.com' }) } > append </button> <button type="button" onClick={() => prepend({ name: 'prependName', email: 'prepend@email.com', }) } > prepend </button> <input type="submit" /> </form> ); };終わりに
ここまで読んでいただきありがとうございます!
少しでもreact-hook-formの理解が深まれば幸いです。自分も書きながら、react-hook-formの便利さが機能が理解できました。
質問、感想、改善点などをコメントでいただけるとモチベーションにつながりますのでよければお願いします!
- 投稿日:2020-11-20T11:01:03+09:00
【React】React Tableで行をクリックした際のonclickイベントハンドラを設定する方法
メモとして残します。
みんな大好き、React Tableですが、行をクリックした際のイベントハンドラの設定方法メモとして残します。
■やり方
下記の通り
getTdProps
プロパティに設定をします。<ReactTable getTdProps={(state, rowInfo, column, instance) => { return { onClick: e => { //if(column.id !== 'col-check'){ // 基本的な処理 // return //} } } }} />ちなみに、
getTrProps
というプロパティもありますが、このプロパティだと、例外的にイベントを発火させたくない列があったときに対応できないため、getTrProps
はあえて、採用しませんでした。
他thといった要素にも設定できるため、公式で確認しましょう。
以上。
- 投稿日:2020-11-20T10:56:28+09:00
ツンデレ湯婆婆はReactで色恋するのか?
あらすじ
みなさま、お腹いっぱいでしょうか?
「湯婆婆ネタはもういらない」と?
いまさら感が強いですが、またまた湯婆婆のラブコメと学ぶ入門記事です。湯婆婆ブームもそろそろ落ち着いてきましたね。
Javaで湯婆婆を実装してみるが元ネタです。難易度☆★★★★
書いたコードの難易度としては、
ProgateのReactコース修了レベルです。
やはり湯婆婆は入門者のゲーミフィケーションに最適ですね。相違点
Reactは既に「React (JavaScript) で湯婆婆を実装してみる」で紹介されています。
後発として差別化をします。
- BabelではなくTypeScriptを使用
- CodePen.ioで形成した湯婆婆を埋め込み
- 書き終えた契約書はReactiveに瞬間移動し湯婆婆の手の中へ
- 契約書はQiitaカラー
#55c500
にして統一感を演出恋する乙女、ツンデレ湯婆婆ちゃん
See the Pen React-Yubaba by ShortArrow (@ShortArrow) on CodePen.
言語による冷静な事務対応の限界
2つの言語を試してみて、気が付いたことがあります。
どの時点まで?田さんに冷静に対応できるのか?
それが言語によって「簡単に調節できるか」が異なるのです。以前書いた記事「Powershellで湯婆婆の召喚魔法を実装」では、
Powershellでした。契約書だよ。そこに名前を書きな。 ��田 フン。��というのかい。贅沢な名だねぇ。 今からお前の名前は��。いいかい、��よ。分かったら返事をするんだ、、�!�!!この時の湯婆婆ちゃんは、
契約書に記載した?田という正式名すら発声できません。
それどころか視界不良に陥り、契約書が見えていないようです。
「デレデレ」過ぎてこっちが恥ずかしいですね。ツンデレ湯婆婆ちゃん
今回の湯婆婆ちゃんは、Javascriptです。
?田さんという名前を発声することができました。フン。?田というのかい。贅沢な名だねぇ。ここまでは「ツン」ですね。
ところがその直後、自分で考えたニックネームを発声できません。今からお前の名前は�だ。いいかい、�だよ。分かったら返事をするんだ、�!!恥ずかしくて「デレ」ています。
「アオハルかよ」って感じですね。
挿絵として「少女漫画風の湯婆婆」が欲しいです。添え字の境界値に気を付けよう
境界値に気を付けなければ、以下の等式が成り立つ恐れがあります。
1 ÷ (名前の長さ + 1) = 湯婆婆が絶句する確率下記の
handleClick()
は湯婆婆の名前を盗むロジックのコアです。
この中で、(this.state.name.length - 1)
として配列の添え字の番号に合わせています。handleClick() { const randamIndex = Math.round(Math.random() * (this.state.name.length - 1)); const madeName = this.state.name.substring(randamIndex, randamIndex + 1); this.setState({ clicked: true, handleName: madeName }); }仮に、
randamIndex
の定義が、const randamIndex = Math.round(Math.random() * this.state.name.length);だとしましょう。
Math.random()
で0~1の範囲を作り出しています。
その結果、名前が御成敗式目
の場合にrandamIndex
の値は0~5となります。
そうすると、randamIndex
の値が0の時はmadeName
が御
で、4の時はmadeName
が目
です。そうなんです。
randamIndex
が5になれば、madeName
の値はヌル文字列です。やはり「マイナス1」って偉大な数字ですね。
冷静な事務対応を執行するには
湯婆婆ちゃんを失恋させる
湯婆婆ちゃんを失恋させるにはサロゲートペアを考慮する必要がありました。
なぜ絵文字を含む文字を1文字ずつに分けるのにArray.fromだけで十分なのか?
という記事で詳細に解説されています。また、Flutterの湯婆婆にサロゲートペア文字を理解させたら可愛くなった?で紹介されていますが、
Dartの場合Charactersクラスを使って簡単に処理することが出来るそうです。※サロゲートという言葉の語感は、石黒研究室のサロゲートが出演するサロゲートという映画をご覧いただくと理解しやすいと思います。
湯婆婆失恋バージョン
See the Pen React-Yubaba-LostLove by ShortArrow (@ShortArrow) on CodePen.
失恋バージョンでは
handleClick()
を書き換えています。handleClick() { const randamIndex = Math.round(Math.random() * (Array.from(this.state.name).length - 1)); const madeName = Array.from(this.state.name)[randamIndex]; this.setState({ clicked: true, handleName: madeName }); }恋する乙女バージョンでは
this.state.name
のlength
プロパティから長さを取得していました。
これをArray.from()
で配列化してから長さを見るようにしています。また、
substring()
を使って切り出していたところを、配列のインデックスで指定して取り出すように変えました。失恋バージョンへの入力
?田さんのほうから、アプローチを仕掛けてみましょう。
ちょっとハートマークが騒がしいですね。失恋バージョンの結果
この通り、全く緊張感の欠片もありません。
淡々と機械的に読み上げる様を見る限り、
微塵も恋している様子はありませんね。環境設定
CodePen.ioで書いています。
CodePen.ioでの設定内容
- 下記CDNでReactをインポート
- ドロップダウンボックスでTypeScriptを選択
- その他はデフォルト設定
まとめ
多言語でも同じ方法で失恋させることが出来る場合とそうでない場合がありそうです。
この記事を読んで「別の言語で失恋をさせてみたいな」と感じてもらえたらハッピーです。Excelsior!
- 投稿日:2020-11-20T10:20:48+09:00
【React.js/Next.js】styled-componentsでrgbaを利用する時に地味に詰まったのでpolishedで解決した
styled-componentsでrgbaの利用で困った
以下のような実装でした。
const Container = styled.header<{ flag: boolean }>` background: ${(props) => props.flag ? rgba(255, 255, 255, 0.07) : rgba(255, 255, 255, 1)}; `;しかし、
名前 'rgba' が見つかりません。
と言われる…。polishedで解決した
polished
を入れれば良かったようでした。$ yarn add -D polishedimport { rgba } from "polished"; const Container = styled.header<{ flag: boolean }>` background: ${(props) => props.flag ? rgba(255, 255, 255, 0.07) : rgba(255, 255, 255, 1)}; `;サンプルっぽいのが以下です。
- 投稿日:2020-11-20T10:11:48+09:00
【React.js/Next.js】スクロールイベントの検知をしてフラグを切り替える
仕様
- スクロール中のイベントを検知したらフラグをTrueにする
- スクロールが止まって定義した時間が経過したらフラグをFalseにする
※サイトへアクセスした時点でフラグが一瞬Trueになっちゃいますが、この実装途中で使わなくなったんで直してないです。
※ただ地味に大変だったので供養したくなりました。実装
スクロール中は透明になるヘッダーを作ってみました。
const DefaultHeader: FC = () => { let timeOutId = 0; const SCROLL_INTERVAL_TIME = 500; const [scrollStatus, setScrollStatus] = useState(false); const makeInactiveScrollStatus = () => { setScrollStatus(false); }; const makeActiveScrollStatus = () => { clearTimeout(timeOutId); setScrollStatus(true); timeOutId = setTimeout(makeInactiveScrollStatus, SCROLL_INTERVAL_TIME); }; useEffect(() => { if (typeof window !== "undefined") { window.addEventListener("scroll", makeActiveScrollStatus); } return () => { if (typeof window !== "undefined" && window.scrollY !== 0) { window.removeEventListener("scroll", makeActiveScrollStatus); } }; }, []); return ( // styled-componentsにflagを投げる <Header scrollStatus={scrollStatus}> // 省略 </Header> ); }; const Header = styled.header<{ scrollStatus: boolean }>` position: sticky; top: 0; height: 5.5rem; background: ${(props) => props.scrollStatus ? rgba(255, 255, 255, 0.07) : rgba(255, 255, 255, 1)}; padding: 0 1rem; @media screen and (min-width: 48.1em) { padding: 0; } `;
- 投稿日:2020-11-20T09:49:34+09:00
if (hoge) {~ 比較演算子を使わないIF文がどこまでを「true」とするのかを徹底的に検証してみた【テキスト版】
前提
if (text) { return true } return falseこれは、私が実務で初めてこの書き方を目にした時、
「えっ、これ比較演算子書き忘れてるやん」って思ったんだけど(バカ、俺のバカ!)こういうちゃんとした書き方があることを知り、
ちょっとした衝撃を受けた時のこと。その後、
私も弱輩ながらこの書き方をマネし初めたが、
色々と上手く行かない…
そんなことから、一体この書き方が
どの時が「true」で
どの時に「false」を返すのか
を検証してみたところから始まる。
*********************
前回の記事で記述したが、
今回は動画にも起こしてみようと思ってたけど、
書いてみたら動画があると見辛そうなレイアウトになったので
次回【動画版】として掲載しようと思う。→前回の記事 【公開】Qiitaに投稿したYoutube動画を自動再生させる!
検証環境
・言語:React.js
※少なくともJavaScript系の言語は同じ結果だと思う。
・コード// この変数にいろんな物を入れてみることで検証 let text = 'hoge' const result = () => { // ↓ここの判定具合を検証する if (text) { console.log('text:', text, true) // 条件一致 return text + ' ←textの中身' } console.log('text:', text, false) // 条件不一致 return 'textになんも入ってないで' } function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p>{result()}</p> {/* ←ここで画面に表示 */} </header> <div></div> </div> ); } export default App;
・表示
見づらいかも知れんが、
画面とコンソールにこのように表示される。
では検証開始!!!!!!!!
1.文字列
その1 中身あるやつ
let text = 'あなたはだんだん「いいね」したくなる'
その2 空欄
let text = ''・画面
・コンソールログ
☆ 結果 = false
この辺は当然そうでしょう。
2.数値
その1 プラスの数値
let text = 1
その2 ゼロ
let text = 0まあこの辺も想定内
その3 マイナスの数値
この辺りから私の心が叫び始める。
「あれぇーゼロより少ないのにtrueなの? なんで?」
どうやらゼロは「無」だけどマイナスは「有」であると判定するようだ。続いて行ってみる。
3.カッコのやつ
エンジニアが「カッコのやつ」とか言ってると怒られるかも知れんが、
ここではあえてこの呼び方で行こうと思う。その1 空配列
let text = []これはtrue?
とりあえず目には見えない「何か」が存在する
ということで納得するとする。
その2 空オブジェクト
let text = {}今度は何か見えた!?どゆこと?
あるの?ないの?
2つ目の「O(オー)」は大文字だし…わけわからんが、
つぎ行ってみよう。
4.真偽値
もうこうなったらまんま入れてみようと思う。
その1 真の値
let text = trueおっ!trueが画面に表示されてるってことは
文字列になってるってことか!?
その2 偽の値
let text = falseまーそれはそうですよね。
5.「無いよ」みたいなやつ
その1 ぬる
let text = null
その2 未定義
let text;んーーーーーーundefined…
より分からんくなってきたねーー
6.ちょっとなんて表現していいか分からないやつ
その1
let text = 0 / 0falseは何となく分かるけどゼロじゃないの!?
NaNって何?
その2
let text = 1 / 0うわぁーまた何か出てきた!
何?Infinityって?
私の世代はDo as Infinityがバッと出てきちゃうのよ。
電卓も「数値じゃありません」って言ってるよ?
怖いわー
その3
let text = 1 / 0これはゼロなんかい!
もう寝る!!!
まとめ
そんなこんなで検証した日はいつもよりも深い眠りについた訳だが、
お陰で覚えることができた。そういうことなので、
「if (hoge) {~ 比較演算子を使わないIF文がどこまでを「true」とするのか」
結論…「とにかく覚える」
これに限る。
こういう判定になる理由を言語化できるならいいが、
そうでないならベタで覚えるしかないのかな。無理してこの書き方にする必要はないんだろうが、
もし使うときは、
特に配列とオブジェクトは気をつけたいところ。そんな気難しいこの書き方、
私は使い続けていこうと思う。なぜなら…
「なんかカッコええやん」
- 投稿日:2020-11-20T00:01:40+09:00
三項演算子の使い方(独り言)
突然なのですが。
プログラミングを書いていて、こんな時はありませんか?・処理の条件をifが使いづらい状況でも使いたい
・スマートに書きたいこんな時は、三項演算子がおすすめです。
では実際にどのような時に使うのか?
どんな記述の仕方なのか?例を見ていきたいと思います。co
const test = "hoge" return( <div>{ test ? "trueの処理" : "falseの処理"}</div> )このような書き方が「三項演算子と呼ばれるものです。」
今回の例でいうと、testという変数にhogeというString型の文字が入っています。
ifの処理でいうと、この処理はtrueと判定されるのでUIに出てくる文字は【trueの処理】とでてきます。上の例を参考にして、もう少し分岐を多くすると
const num = 100 <div>{num < 99 ? "numは99より小さいです" : num > 1000 ? "numは1000より大きいです" : num === 100 ? "numは100です" : "hoge"}</div>複雑にはなりますが、こんな書き方もできます。ちなみにこの例だと「numは100ですと出力されます」
三項演算子は使うことも多いですがあまり分岐が複雑だと、後で大変になるので
できるだけ、簡単な分岐をスマートに実装させたいときにお勧めです!ではまた。