- 投稿日:2021-02-28T23:41:37+09:00
Reduxの環境を5分で構築しよう!
Reduxの環境を5分で構築しよう!
Reduxの環境を5分で構築する方法を記します。
Reduxとは
Redux(リダックス)とは、Reactにおけるコンポーネントの状態を管理する機能を提供するライブラリです。
今回のゴール
Node.jsをインストールし、Reactプロジェクトを作成し、ReactプロジェクトにReduxをインストールすること
OS情報
- Windows10
構築手順
Node.jsをインストール
https://nodejs.org/ja/にアクセスし、推奨版をダウンロードします。
標準設定でインストールする場合は、デフォルトのまま「Next」をクリックすればよいです。Reactプロジェクトを作成
コマンドプロンプトを開き、任意のディレクトリにReactプロジェクトを作成します。
npx create-react-app <project_name>
と入力します。Reduxのインストール
作成したプロジェクトにReduxをインストールします。
まず作成したプロジェクトのルート階層へ移動します。
npm install --save redux
と入力します。React Reduxのインストール
「React Redux」というパッケージをインストールすることで、ReactプロジェクトでReduxを使用できるようになります。
npm install --save react-redux
と入力します。まとめ
以上の手順でReduxが使用できるようになりました。
新しくプロジェクトを作成する際には、1の手順は飛ばし、2~4の手順で作成しましょう。おわり
- 投稿日:2021-02-28T19:46:29+09:00
React + Firebase Authenticationでログイン機能を簡単に実装する
はじめに
FirebaseのAuthenticationを使って、超簡単にReactアプリケーションのログイン(認証)機能を実装します。
いくつか既に記事はあるかと思いますが、ちょっと古かったり、動かなかったりしたので、記事に残してきます。
ご参考になれば幸いです!前提
npx create-react-app
等でReactプロジェクトの作成が完了していること- Firebaseコンソール上でのプロジェクト作成、登録が完了していること
Firebase上での作業がまだの方は、下記などを参考に進めてください。
https://yanou.jp/react-firebase-authentication/ざっくり流れ
- Firebase SDKをReactアプリケーションで読み込む
- ユーザー登録、ログイン機能を実装する
なお、記事が長くならないように、
※CSSは書いておりません
※ユーザー登録とログインボタンが同一コンポーネント内にありますがご容赦ください...それでは詳細を見ていきましょう。
手順①Firebase SDKをReactアプリケーションで読み込む
SDKの場所は、下記の設定マーク→プロジェクトを設定→ページ下部にあります。
1. firebaseをインストール
npm i firebase2. src配下にfirebaseファイルを作成する
firebaseConfig.tsimport firebase from 'firebase/app' import 'firebase/auth' const firebaseConfig = { apiKey: "hogehoge", authDomain: "hoge.firebaseapp.com", projectId: "hoge", storageBucket: "hoge.com", messagingSenderId: "hoge", appId: "hogehogehoge" } const fire = firebase.initializeApp(firebaseConfig) export default fire手順②ユーザー登録、ログイン機能を実装する
App.tsximport React, { useState } from "react"; // firebaseConfigからfireをインポートする import fire from "./firebaseConfig"; export const App: React.FC = () => { //ユーザー管理用state const [user, setUser] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); //ログインメソッド const handleLogin = () => { fire .auth() .signInWithEmailAndPassword(email, password) }; //ユーザー登録メソッド const handleSignup = () => { fire .auth() .createUserWithEmailAndPassword(email, password) }; //ログインしているかをチェックするメソッド const isLogin = () => { fire.auth().onAuthStateChanged((user) => { if (user) { setUser(user); } else { setUser(""); } }); }; //レンダー後にisLoginを実行する useEffect(() => { isLogin(); }, []); return ( <div> {/* userのstateでコンポーネントを切り替えている */} { user ? ( <div> {/* ログイン後に表示したいコンポーネントを書く */} </div> ) : ( <div> <label htmlFor="email">メールアドレス</label> <input id="email" type="text" placeholder="メールアドレス" value={email} onChange={(e) => setEmail(e.target.value)} /> </div> <div> <label htmlFor="password">パスワード</label> <input id="password" type="password" placeholder="パスワード" value={password} onChange={(e) => setPassword(e.target.value)} /> </div> <div> <button type="button" onClick={handleSignup} > ユーザー登録する </button> <button type="button" onClick={handleLogin} > ログインする </button> </div> )} </div> ); };これで完了です?
終わりに
これだけの記述で認証機能が実装できるのは本当に楽ですね。
長い記事にしたくなかったので、コードの詳細は端折っております。何か質問があれば、コメントください!
- 投稿日:2021-02-28T14:23:28+09:00
毎日投稿(React)TODOアプリ学習
React(TODOアプリ作成)
今日もReactの学習しました。U-demyの動画学習です。
以下オススメです!!モダンJavaSciptの基礎から始める挫折しないためのReact入門
https://www.udemy.com/course/modern_javascipt_react_beginner/昨日、Reactの基本的な書き方など学んだ上で、
今回はReactでTODOアプリを作成しました。動画学習の進め方としては、以下のような流れが一番です!
機能追加などセクション毎に分かれいるので、まずは自分で実装してから出来なかったら、
動画を見る!という流れで実践したら、理解度が格段に違ってきます!例
・タスク追加機能
・タスク削除機能
・タスク完了機能など、、、まずは自分で考えて実装となると、時間がかかりますが、
これは絶対にすべきですね。どうやって、引数を渡すのだろう?
どうやってuseStateを更新すればいいかなど考えて実装するうちに、わからないにぶち当たります。
そして答えとなる動画を見ると「あ!こんな風にやるのか!」面白くなります!私の場合、削除ボタンを押すとタスクが削除される時に、どうやったら、引数を渡すかわからなくなり、答えとなる動画を見ました。
最初の実装⬇️
<button onClick={onClickRemove(index)}>削除</button>答え⬇️
<button onClick={() => onClickRemove(index)}>削除</button>なるほど!ということ実感出来たので、とても楽しかったです!
感想
JavaScriptで作ったTODOアプリより、Reactで作ったTODOの方格段にコード量が少なく、また使い回しが効くなどメリットがありすぎるので最高でしたね。
まだまだReact初心者ですが、これから学習していきます!以上、ありがとうございました。
- 投稿日:2021-02-28T14:09:23+09:00
モダンJavascript基礎 スプレッド構文の世界で一番分かり易い解説
はじめに
こんにちは! プログラミング学習中のタイソンです!
今回はES6で追加されたスプレッド構文について学んだことを、分かり易さ重視で解説していこうと思います。わかりにくいところ、間違っているところなどがございましたら、お気軽にご指摘ください!スプレッド構文とは
「...」
このようなドットが3つ並んだコードを皆さんは見たことがあるでしょうか。この3つのドットをスプレッド構文と言います。このスプレッド構文を一言で表すと順番に処理して展開する
というものです。
この言葉だけではあんまりピンと来ないですよね。
大丈夫です!コードでわかりやすく解説していきます!配列の展開
const arr1 = [1,2]; console.log(arr1);このような配列があるとします。この配列arr1の中身をconsole.log()で見てみましょう。
もちろんこのような結果になります。次はスプレッド構文を使って配列の中身を見てみましょう。const arr1 = [1,2]; //スプレッド構文で配列の中身をみる↓ console.log(...arr1);
先ほどはconsole.logの結果が配列だったのに対して、今回は実際の1と2という値になっていることがわかります。このようにドットを3つ書いてその後に配列を指定すると、配列の中身を順番に処理して展開します。
ちょっと複雑でわかりにくいですよね。もう少し分かり易い例で解説していきます。
//配列の作成↓ const arr1 = [1,2]; //アロー関数の作成↓ const sumFunc = (num1, num2) => console.log(num1 + num2); //実引数に配列の値を指定↓ sumFunc(arr1[0], arr1[1]);このような配列とアロー関数を作成して、コンソールで結果をみてみましょう。
このような結果になりました。
次は同じ式にスプレッド構文を使って引数を渡していこうと思います。const arr1 = [1,2]; const sumFunc = (num1, num2) => console.log(num1 + num2); sumFunc(...arr1);結果はこのようになります。
先ほどと同じ結果になりましたね。先ほどの記述と、このスプレッド関数を使った例は同じ動きをしているということになります。配列に対してドットが3つあったら、
順番に処理して展開
と頭の中で変換してください。配列をひとつにまとめる
前の章はスプレッド構文の配列の展開についてでしたが、次は配列をひとつにまとめるというスプレッド構文の使い方について解説しようかと思います。
//配列を用意↓ const arr2 = [1, 2, 3, 4, 5]; //配列をスプレッド構文を使って分割代入↓ const [num1, num2, ...arr3] = arr2;このような配列を用意し、分割代入していきます。
console.log()でnum1とnum2の値の中身を見てみましょう。分割代入が何かわからない方はこちらの記事を読んでください。↓
const arr2 = [1, 2, 3, 4, 5]; const [num1, num2, ...arr3] = arr2; console.log(num1); console.log(num2);
このような結果になりました。次にarr3の中身をconsole.logで見てみましょう。1と2以外の数字が配列としてまとめられていました。このようにスプレッド構文は配列をまとめることもできるのです。
配列のコピーや結合
次は配列のコピーや結合について解説していきます。とても大事なところなのでしっかり勉強していきましょう。
const arr4 = [10, 20]; const arr5 = [30, 40];まずこのような2つの配列を用意します。そして新しくarr6という配列をスプレッド構文で用意します。
const arr4 = [10, 20]; const arr5 = [30, 40]; const arr6 = [...arr4];console.log(arr6)で、配列arr6の中身を見てみましょう。
このようにarr4の中身がコピーされました。このようにスプレッド構文は配列のコピーにも使うことができます。次にこれの応用として、スプレッド構文の結合について見ていこうと思います。先ほどのコードを以下のように変更します。
const arr4 = [10,20]; const arr5 = [30,40]; const arr6 = [...arr4, ...arr5]; console.log(arr6);この配列arr6の中身はどうなっているのでしょうか。console.logで見てみましょう。
配列arr4とarr5の中身が結合されましたね。
このようにスプレッド構文を使うと配列の結合も容易に行うことができます。参照渡しについて
しかし配列のコピーをするだけなら、以下のように
「配列を=でそのままコピーすればいいじゃん!」
と思う人がいるかもしれません。
const arr4 = [10,20]; //arr4をコピーしたarr8を定義 const arr8 = arr4;console.log(arr8)の中身を見てみましょう。
このようにコピーができていますね。しかしこれには問題があります。
参照が引き継がれてしまっているのです。
以下の例を見てみましょう。const arr4 = [10,20]; const arr8 = arr4; //arr4をコピーしたarr8の最初の値を変更する↓ arr8[0] = 100; console.log(arr8); console.log(arr4);console.log()でarr8とarr4の中身を見て見ましょう。
arr8の値だけを100に変えたつもりなのに、元のarr4の値も100に変わってしまっています。このようなことが起こってしまうので、配列のコピーをする際はスプレッド構文を使いましょう。スプレッド構文で同じことをやってみましょう。
const arr4 = [10,20]; const arr8 = [...arr4]; arr8[0] = 100; console.log(arr8); console.log(arr4);console.logでarr8とarr4の中身を見てみましょう。
arr4の値を変えずに、arr8の値だけを変えることができました。なぜこのようなことが起こってしまうのかは以下の記事を参考にしてください。
最後に
いかがだったでしょうか。スプレッド構文はjavascriptだけでなく、Reactなどでも使っていくのでしっかり理解を深めておきましょう。見ていただきありがとうございました。
- 投稿日:2021-02-28T12:54:54+09:00
useRefの使い方よくわからないから学んでみる
概要
実務にてReactHooksを使うことが多いので、
「今こそhooksについて学びを深めるべきだ!!」
ということでuseRefについて学習した内容を
共有すべく記事にまとめます〜!useRefとは
useRef は元々あった createRef の hooks 版です。その名の通り、DOMに対する参照を持つために使われるのが主な目的です。
とのことです。
ここで僕が気になったのは「createRefって?」createRefとは、v16.3.0以上で利用可能なメソッドです。
ref属性に渡して、対象のDOMを参照できる、みたいなことです。
⬇︎例// classコンポーネント内 constructor(props) { this.ref = React.createRef(); } render() { <div ref={this.ref} > <span>text</span> <span>text</span> </div> } componentDidMount() { console.log(this.ref.current.clildNodes); // output: NodeList(2) [span, span] }ここにおけるReact.createRef()→useRef()ってことです。
つまり、書き換えると以下のようになります。
⬇︎例// 関数コンポーネント内 const ref = useRef(); useEffect(() => { console.log(ref.current.childNodes); // output: NodeList(2) [span, span] }); return ( <div ref={ref} > <span>text</span> <span>text</span> </div> );useEffectの部分は、componentDidMountに対応しており、マウント時だと思っていただければOKです!
詳細
useRefは、コンポーネントがマウントされる瞬間からアンマウントされるまで存在し続けます。
つまり、レンダーが起こってもアンマウントされてなければ前の値が入ったままになる、ということです。const list1 = []; const list2 = []; // 関数コンポーネント内 const [count, setCount] = useState(0); const onClick = () => { setCount(c => c + 1); }; const obj1 = {}; list1.push(obj1); if (list1.length >= 2) { console.log(list1[list1.length - 2] === list1[list1.length - 1]); // これは、現在のobj1と1つ前のレンダーのobj1を比べています。 output: false } const obj2 = useRef(); list2.push(obj2); if (list2.length >= 2) { console.log(list2[list2.length - 2] === list2[list2.length - 1]); // これは、現在のobj2と1つ前のレンダーのobj2を比べています。 output: true } return ( <div ref={ref} > <button type="button" onClick={onClick}> count up </button> {count} </div> );この例のように、Ref以外の一般的なオブジェクトはレンダーごとに「新しいオブジェクトを生成している」ことがわかります。
逆に、useRefはレンダーされても「同じオブジェクト」を使い続けています。
つまりは、useRefは「レンダーと値が一対一になっていない」ということです。refの値が書き変わってもレンダーは起きないし、レンダーが起こってもrefの値は書き変わらない。
ここがuseRefの特徴でしょう。しかし、この特徴はReactのコンセプトから外れてしまってるので非推奨というか多用すべきではないようです。
とはいえ使っていい例もあるようで、
それは、
- テキストの選択、フォームへのフォーカスもしくはメディア(動画、音声)の再生の制御
- アニメーションを発火させる場合
- サードパーティのDOMコンポーネントを組み込む場合
と公式ドキュメントにも記載されてますね。
だからこそ、しっかりと理解して使うことができれば便利に開発できるようになるでしょう!まとめ
今回はuseRefについて学んでみました。
僕からしたらかなり便利だなと感じます。しかし、そういったものこそしっかりと理解し、特徴を踏まえた上で使用することが大切だなと。
基本的にはこの記事を書く上で理解できたのでどんどん使って慣れようかなと思います!
ガンガン成長していきましょう!!参考資料
https://ja.reactjs.org/docs/hooks-reference.html#useref
https://qiita.com/seya/items/6bbfa3f9d489809ccb2c
https://numb86-tech.hatenablog.com/entry/2019/12/05/111342
- 投稿日:2021-02-28T08:45:36+09:00
Next.jsとtailwindを使ってダークモードをサクッと実装する
初めに
最近、
Next.js
にTailwind CSS
で作成した個人ブログにダークモード機能を追加したので、その備忘録として。Tailwindのダークモードを有効化する
TailwindのV-2からダークモードが組み込まれており、ダークモード時に指定の色に切り替えてくれるようになりました。
ダークモードを有効化するのは簡単で、
tailwind.config.js
のdarkMode
にclass
かmedia
を指定するだけです。media
を指定した場合は、OSの設定に基づいて、自動でdarkモードに切り替えてくれますが、今回はユーザーがページ上でON/OFFの切り替えが出来るようにしたいので、class
を指定して、ダークモードを有効化させます。詳細はTailwindのドキュメントに記載されてます。
tailwind.config.jsmodule.exports = { purge: [], darkMode: 'class', //ダークモードを有効化する theme: { extend: { colors: { darkgrey: '#222831', //darkModeで使用したい色を拡張定義 }, }, }, variants: { extend: { }, }, plugins: [], };next-themesをインストールする
次にボタンを押下した際に、htmlタグのclassに対して
dark
をアクティブ/非アクティブにするための実装が必要になります。htmlタグのclassにdarkを付与できるようにする必要がある<html class="dark">Tailwindのダークモードは、htmlタグのclassに
dark
が付与されてる場合、以下のような記述で、ダークモード時の色を指定する事が出来ます。dark<div className="dark:bg-darkgrey dark:text-white">ドキュメントには現在のモードをlocalStorageに保存する方法が紹介されていますが、ここでは、
next-themes
というライブラリを使って実装を進めます。next-themesをインストール$ npm install next-themes # or $ yarn add next-themesライブラリをインストールしたら、
_app.js
にThemeProvider
をimportします。
attributeにはclass
を指定しましょう。(※Tailwindのダークモードを使用するには、この記述が必要です)_app.tsximport '../../styles/globals.css'; import '../../styles/common.css'; import { AppProps } from 'next/app'; import { ThemeProvider } from 'next-themes'; function MyApp({ Component, pageProps }: AppProps) { return ( <ThemeProvider attribute="class"> <Component {...pageProps} /> </ThemeProvider> ); } export default MyApp;ダークモードを切り替えるボタンを作成する
切り替えるボタンは以下のようにしました。
useTheme()
フックを利用して、テーマの切り替えを制御します。import { useTheme } from 'next-themes'; const { theme, setTheme } = useTheme(); // レンダー後かを判定 const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); <button aria-label="DarkModeToggle" type="button" className="p-3 h-12 w-12 order-2 md:order-3 absolute left-2/4 transform -translate-x-2/4 md:relative md:left-0" onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')} > {mounted && ( <> {theme === 'dark' ? ( <Moon height={'25'} width={'25'} /> ) : ( <Sun height={'25'} width={'25'} /> )} </> )} </button>後は、Tailwindのいつもの書き方でダークモード時の色を指定してあげるだけです。
dark<div className="relative mb-10 dark:bg-darkgrey"> ... </div>以上、これでお手軽にダークモードを実装することが出来ます。
- 投稿日:2021-02-28T04:57:03+09:00
【JavaScript】 海外Teck系YouTuberを真似てみた!(partⅢ) Task Tracker App | React JS
1,はじめに
この記事は、海外Teck系YouTuberの動画を参考に、同じプロジェクトを作成してみたものになります!
簡単にですが、動画を通して学べた技術や知識をまとめました!
今回の完成品は、下記に載せています
2,学んだこと
今回はさらに発展して、Reactのモジュール(RouteやLink)なども使ってページ遷移も行いました!
async await の技術も使っているので、内容が盛り沢山でした今回は、
① async await の使い方
② JSONサーバーをローカルの5000portに立ち上げて、そこにデータを保存する方法
③ フォーム画面から値を受け取る方法
④ onClick、 onToggle、 onDelete、 onDoubleClick、 onChangeasync await の考え方を理解していないと、終盤についていくのが大変でした
3,参考動画
4,完成品です
左がJSONサーバーの様子で、右がブラウザ表示になります!
5、最後に
今回は、かなり手ごたえがありました!
特にasync await の考え方をもう一度振り返る必要があると感じました!
次回も、Reactを使ったプロジェクトにトライしてみます。
- 投稿日:2021-02-28T04:57:03+09:00
【JavaScript】 海外Teck系YouTuberを真似てみた!(part 3) Task Tracker App | React JS
1,はじめに
この記事は、海外Teck系YouTuberの動画を参考に、同じプロジェクトを作成してみたものになります!
簡単にですが、動画を通して学べた技術や知識をまとめました!
今回の完成品は、下記に載せています
2,学んだこと
今回はさらに発展して、Reactのモジュール(RouteやLink)なども使ってページ遷移も行いました!
async await の技術も使っているので、内容が盛り沢山でした今回は、
① async await の使い方
② JSONサーバーをローカルの5000portに立ち上げて、そこにデータを保存する方法
③ フォーム画面から値を受け取る方法
④ onClick、 onToggle、 onDelete、 onDoubleClick、 onChangeasync await の考え方を理解していないと、終盤についていくのが大変でした
3,参考動画
4,完成品です
左がJSONサーバーの様子で、右がブラウザ表示になります!
5、最後に
今回は、かなり手ごたえがありました!
特にasync await の考え方をもう一度振り返る必要があると感じました!
次回も、Reactを使ったプロジェクトにトライしてみます。
- 投稿日:2021-02-28T01:29:21+09:00
[React]フォーム入力の度にフォーカスが外れるときに確認すべきこと2選
はじめに
Reactでフォーム入力を扱ってる時,こんな風に入力の度にフォーカスが外れて連続入力ができない現象に遭遇して少しハマったのでまとめました。(以下のGIFで入力のたびに青枠が外れてるのがわかるかと思います)
問題のコード と CodeSandbox
CodeSandbox:
https://codesandbox.io/s/billowing-cherry-fy3dx?file=/src/App.jsimport React from "react"; function App() { const [inputValue, setInputValue] = React.useState(""); const handleInput = (event) => { event.preventDefault(); setInputValue(event.target.value); }; const InputForm = () => { return ( <label htmlFor="input2"> <input id="input2" type="text" defaultValue={inputValue} onChange={handleInput} /> </label> ); }; return ( <div className="App"> <InputForm /> </div> ); } export default App;原因
子コンポーネントを親コンポーネントのブロックの中で定義していたことが原因でした。
親コンポーネント(App)が再描画される度に、小コンポーネント(InputForm)を再定義、再描画していたので、Reactがもともとフォーカスを当てていたエレメントが破棄され、新しいエレメントとしてのinputが作成されていたためにフォーカスが外れていたようです。
解決方法:
子コンポーネントの定義をコンポーネントの外にすればOKです!
CodeSandbox
https://codesandbox.io/s/input-fixed-9hphl?file=/src/App.jsimport React from "react"; function App() { const [inputValue, setInputValue] = React.useState(""); const handleInput = (event) => { event.preventDefault(); setInputValue(event.target.value); }; return ( <div className="App"> <InputForm value={inputValue} handler={handleInput} /> </div> ); } export default App; //親コンポーネントの外側に定義 別ファイルでも可 const InputForm = (props) => { return ( <label htmlFor="input2"> <input id="input2" type="text" defaultValue={props.value} onChange={props.handler} /> </label> ); };別の原因の可能性
実は原因を調べる過程で、同じ現象が起きる状況があることがわかりました。
それは mapで複数コンポーネントを描画した際のkeyに描画ごとに変化しうる値を使っている場合です
こちらも一例目と同じように、Reactがフォーカスをあてていたinputのkeyが存在しなくなり、新規エレメントとして描画していることがフォーカスが外れる原因みたいです。バグコードの例
このコードでは、keyをフォームごとにユニークにするために入力値を用いています。(これだとユニークになるとは限らないですが、一例です)
Codesandbox:
https://codesandbox.io/s/multiple-inputs-with-a-bug-qt7lu?file=/src/App.jsimport React from "react"; function App() { const [inputValueArr, setInputValueArr] = React.useState([ "input1", "input2", "input3" ]); const handleInput = (event, index) => { event.preventDefault(); const newInputValueArr = [...inputValueArr]; newInputValueArr[index] = event.target.value; setInputValueArr(newInputValueArr); }; return ( <div className="App"> {inputValueArr.map((value, index) => ( <label key={`hoge_${value}`} htmlFor="input1"> keyにバリューを使っている時 <input id={`input_${value}`} type="text" defaultValue={value} onChange={(e) => handleInput(e, index)} /> </label> ))} </div> ); } export default App;修正後の例
上記コードではkeyが入力のたびに変化してしまうので、フォームごとに固定で固有なkeyをつけてあげます。ここではindexを使います。
Codesandbox:
https://codesandbox.io/s/multiple-inputs-fixed-8wcsi?file=/src/App.jsimport React from "react"; function App() { const [inputValueArr, setInputValueArr] = React.useState([ "input1", "input2", "input3" ]); const handleInput = (event, index) => { event.preventDefault(); const newInputValueArr = [...inputValueArr]; newInputValueArr[index] = event.target.value; setInputValueArr(newInputValueArr); }; return ( <div className="App"> {inputValueArr.map((value, index) => ( <label key={index} htmlFor="input1"> keyにバリューを使っている時 <input id={`input_${value}`} type="text" defaultValue={value} onChange={(e) => handleInput(e, index)} /> </label> ))} </div> ); } export default App;おわりに
最初は浅いコピーやら深いコピーやら絡みのStateの更新の仕方の問題かと思いましたが、もっと初歩的な話でした。公式ドキュメント読んだら書いてあるのかもしれないですね。この記事がどなたかのお役に立てれば幸いです!
参考
- 投稿日:2021-02-28T01:29:21+09:00
[React]フォーム入力の度にフォーカスが外れてしまうときに確認すべきこと2選
はじめに
Reactでフォーム入力を扱ってる時,こんな風に入力の度にフォーカスが外れて連続入力ができない現象に遭遇して少しハマったのでまとめました。(以下のGIFで入力のたびに青枠が外れてるのがわかるかと思います)
問題のコード と CodeSandbox
CodeSandbox:
https://codesandbox.io/s/billowing-cherry-fy3dx?file=/src/App.jsimport React from "react"; function App() { const [inputValue, setInputValue] = React.useState(""); const handleInput = (event) => { event.preventDefault(); setInputValue(event.target.value); }; const InputForm = () => { return ( <label htmlFor="input2"> <input id="input2" type="text" defaultValue={inputValue} onChange={handleInput} /> </label> ); }; return ( <div className="App"> <InputForm /> </div> ); } export default App;原因
子コンポーネントを親コンポーネントのブロックの中で定義していたことが原因でした。
親コンポーネント(App)が再描画される度に、小コンポーネント(InputForm)を再定義、再描画していたので、Reactがもともとフォーカスを当てていたエレメントが破棄され、新しいエレメントとしてのinputが作成されていたためにフォーカスが外れていたようです。
解決方法:
子コンポーネントの定義をコンポーネントの外にすればOKです!
CodeSandbox
https://codesandbox.io/s/input-fixed-9hphl?file=/src/App.jsimport React from "react"; function App() { const [inputValue, setInputValue] = React.useState(""); const handleInput = (event) => { event.preventDefault(); setInputValue(event.target.value); }; return ( <div className="App"> <InputForm value={inputValue} handler={handleInput} /> </div> ); } export default App; //親コンポーネントの外側に定義 別ファイルでも可 const InputForm = (props) => { return ( <label htmlFor="input2"> <input id="input2" type="text" defaultValue={props.value} onChange={props.handler} /> </label> ); };別の原因の可能性
実は原因を調べる過程で、同じ現象が起きる状況が他にもあることがわかりました。
それは mapで複数コンポーネントを描画した際のkeyに描画ごとに変化しうる値を使っている場合です
こちらも一例目と同じように、Reactがフォーカスをあてていたinputのkeyが存在しなくなり、新規エレメントとして描画していることがフォーカスが外れる原因みたいです。バグコードの例
このコードでは、keyをフォームごとにユニークにするために入力値を用いています。(これだとユニークになるとは限らないですが、一例です)
Codesandbox:
https://codesandbox.io/s/multiple-inputs-with-a-bug-qt7lu?file=/src/App.jsimport React from "react"; function App() { const [inputValueArr, setInputValueArr] = React.useState([ "input1", "input2", "input3" ]); const handleInput = (event, index) => { event.preventDefault(); const newInputValueArr = [...inputValueArr]; newInputValueArr[index] = event.target.value; setInputValueArr(newInputValueArr); }; return ( <div className="App"> {inputValueArr.map((value, index) => ( <label key={`hoge_${value}`} htmlFor="input1"> keyにバリューを使っている時 <input id={`input_${value}`} type="text" defaultValue={value} onChange={(e) => handleInput(e, index)} /> </label> ))} </div> ); } export default App;修正後の例
上記コードではkeyが入力のたびに変化してしまうので、フォームごとに固定で固有なkeyをつけてあげます。ここではindexを使います。
Codesandbox:
https://codesandbox.io/s/multiple-inputs-fixed-8wcsi?file=/src/App.jsimport React from "react"; function App() { const [inputValueArr, setInputValueArr] = React.useState([ "input1", "input2", "input3" ]); const handleInput = (event, index) => { event.preventDefault(); const newInputValueArr = [...inputValueArr]; newInputValueArr[index] = event.target.value; setInputValueArr(newInputValueArr); }; return ( <div className="App"> {inputValueArr.map((value, index) => ( <label key={index} htmlFor="input1"> keyにバリューを使っている時 <input id={`input_${value}`} type="text" defaultValue={value} onChange={(e) => handleInput(e, index)} /> </label> ))} </div> ); } export default App;おわりに
最初は浅いコピーやら深いコピーやら絡みのStateの更新の仕方の問題かと思いましたが、もっと初歩的な話でした。公式ドキュメント読んだら書いてあるのかもしれないですね。この記事がどなたかのお役に立てれば幸いです!
参考