20220111のReactに関する記事は6件です。

[Solved] Install Old Version React

When the latest React is 5.0.0, I encountered an error when I needed to install an older version of React (4.0.3). npx create-react-app@4.0.3 app-name > You are running `create-react-app` 4.0.3, which is behind the latest release (5.0.0). > We no longer support global installation of Create React App. It was written that npx clear-npx-cache would solve the problem, but it did not. However, I was able to install it normally with the following method. npx create-react-app app-name --scripts-version 4.0.3 最新のReactが5.0.0の時に、古いバージョンのReact(4.0.3)をインストールする必要が出てきて、エラーに遭遇。 npx create-react-app@4.0.3 app-name > You are running `create-react-app` 4.0.3, which is behind the latest release (5.0.0). > We no longer support global installation of Create React App. npx clear-npx-cache で解決するとか書かれてたけど解決せず。 でも、次の方法で普通にインストールできた。 npx create-react-app app-name --scripts-version 4.0.3
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

useRefで連想配列を使う(Typescript)

タイトル通りですが、意外に連想配列を渡す例がネットで見つからなくて困ったので、メモ程度に書いています。 App.tsx type TodoType = { id: number; todo: string; isDone: boolean; }; const App: React.FC = () => { const [inputText, setInputText] = useState<string>(""); const text = useRef<TodoType[]>([]); const changeText = () => { text.current.push({ id: text.current.length++, todo: inputText, isDone: false }); }; const onChange = (e: React.ChangeEvent<HTMLInputElement>): void => { setInputText(e.target.value); }; return ( <> <input type="text" onChange={onChange} value={inputText} /> <button onClick={changeText}>Change</button> <p> {text.current.map((i) => ( <li key={i.id}>{i.todo}</li> ))} </p> </> ); }; export default App;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Emotion] cssプロパティでエラーが出た場合の対処法

初めに emotionのバージョンは11です。 11から破壊的な変更が入っているので、それ以前のバージョンを使用してる人は要注意。 以下に簡単なコンポーネントサンプルを示しますが、pタグに渡しているcssプロパティの箇所で「そんなプロパティないよ」とエラーが表示されるケースがあります。 Sample.tsx import { css } from "@emotion/react"; const Text = css({ color: "red", }); export const Emotion = () => { return ( <Container> <p css={{ Text }}>Emotion</p> <button>Click</button> </Container> ); }; 対処方法 tsconfig.jsonに以下追記する。 tsconfing.json { "compilerOptions": { . . . "jsx": "react-jsx", "types": ["@emotion/react/types/css-prop"], // 追記 }, "include": ["src"] } もしくは、**.d.tsを定義する src/types/emotion.d.ts /// <reference types="@emotion/react/types/css-prop" /> 以上。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JSのデータタイプのメカニズムとそれによっておこる現象(useCallback React.memo)

今回の記事の内容 データタイプについて コピーのメカニズム 参照の同一性 レンダリングについて データタイプ JSのデータタイプには二種類 プリミティブ型 メソッドを持たないデータ型 string number BigInt boolean undefined symbol オブジェクト型 プロパティとメソッドの集まり array object function RegExp  JSのコピーのメカニズム プリミティブ型 JSで変数などを宣言したら、その変数はパソコンのメモリに保存される。プリミティブ型の変数はコピーする際に新しいメモリ空間を確保して独立的な値を保存。 let string = "りんご"; //←独立して保存される let newString = string; newString = "apple" stringをコピーしてnewStringという変数を生成。そしてnewStringの値を変更。 console.log(string); //りんご console.log(newString); // apple それでも原本のstringには何の影響もない。これは違うメモリに保存されているため。 オブジェクト型 プリミティブタイプのように新しいメモリに保存するのではなく、原本のメモリアドレスを渡される。つまり、原本とコピー本が同じメモリに保存されている同じデータを共有する。 const object = { name: "apple" }; const newObject = object; newObject.name = "banana" console.log(object); // {name: "banana"} console.log(newObject); // {name: "banana"} プリミティブ型と同じように変更すると、このようになる これは同じデータを共有しているからこうなる。  参照の同一性 これどうなる? [] === [] {} === {} (() => {}) === (() => {}) 0 === 0 "string" === "string" true === true false === false //false [] === [] {} === {} (() => {}) === (() => {}) //true 0 === 0 "string" === "string" true === true false === false こっから本編  レンダリングについて useCallback useCallbackはメモ化されたコールバックを返すHook。 依存配列(=[deps] コールバック関数が依存している要素が格納された配列)の要素のいずれかが変化した場合のみ、メモ化した値を再計算する。 メモ化とは? コストが高い呼び出しの結果を保存し、同じ入力が再び発生したときにキャッシュされた結果を返すことによってプログラム実行速度を向上させる技術。 公式では 不必要なレンダーを避けるために参照の同一性を見るよう最適化されたコンポーネントにコールバックを渡す場合に便利 と書かれている。 逆に言うたら 「参照の同一性を見るよう最適化されたコンポーネントにコールバックを渡す場合じゃないと別に要らない」 となる。 ここで基礎的な知識として Reactコンポーネントは自分のstateが変更されたり、親コンポーネントから渡されるpropsが変更された場合再レンダリングされる。 コンポーネントが再レンダリングされると、その中で宣言されている関数や変数は以前保存されていたメモリを空けて新しいメモリに再び保存される(garbage collection)。 これらの知識から分かること こんなボタンコンポーネントがあったとする const CountButton = function CountButton({ onClick, count }) { return <button onClick={onClick}>{count}</button>; }; function DualCounter() { const [count1, setCount1] = React.useState(0); const increment1 = () => setCount1(c => c + 1); const [count2, setCount2] = React.useState(0); const increment2 = () => setCount2(c => c + 1); return ( <> <CountButton count={count1} onClick={increment1} /> <CountButton count={count2} onClick={increment2} /> </> ); } DualCounterのstateであるcount1が変更される DualCounterが再レンダリングされる DualCounterの中の変数や関数(count1、setCount1、increment1など)たち全部がもともと保存されてたメモリを空けて新しいメモリに保存される CountButtonは引数で渡される変数と関数の変更チェック increment1とincrement2がオブジェクト型のため、保存されたメモリが変わったことで新しいやつだと判断 CountButton両方とも再レンダリングされる。 結局一つのCountButtonを押しただけなのにCountButtonが全部再レンダリングされてしまう。 次 こんなコンポーネントがあったとする const CountButton = function CountButton({ onClick, count }) { return <button onClick={onClick}>{count}</button>; }; function DualCounter() { const [count1, setCount1] = React.useState(0); const increment1 = React.useCallback(() => setCount1(c => c + 1), []); const [count2, setCount2] = React.useState(0); const increment2 = React.useCallback(() => setCount2(c => c + 1), []); return ( <> <CountButton count={count1} onClick={increment1} /> <CountButton count={count2} onClick={increment2} /> </> ); } どうなる? まだ全部再レンダリングされる 今のCountButtonたちは渡される引数に変更がなくても親であるDualCounterが再レンダリングされたので自分自身も再レンダリングされた。 これが子コンポーネントを最適化しなければならない理由。 答えはこう const CountButton = React.memo(function CountButton({ onClick, count }) { return <button onClick={onClick}>{count}</button> }) function DualCounter() { const [count1, setCount1] = React.useState(0) const increment1 = React.useCallback(() => setCount1(c => c + 1), []) const [count2, setCount2] = React.useState(0) const increment2 = React.useCallback(() => setCount2(c => c + 1), []) return ( <> <CountButton count={count1} onClick={increment1} /> <CountButton count={count2} onClick={increment2} /> </> ) } React.memoはパフォーマンス最適化のための高階コンポーネント(HOC, higher-order component)で,props の変更のみをチェックする。 高階コンポーネントとは、コンポーネントを引数としてもらって新しいコンポーネントを返す関数。React.memoの場合は引数としてコンポーネントをもらい、最適化されたコンポーネントを返す。 もしあるコンポーネントが同じ props を与えられたときに同じ結果をレンダーするなら、結果を記憶してパフォーマンスを向上させるためにそれを React.memo でラップすることができます。つまり、React はコンポーネントのレンダーをスキップし、最後のレンダー結果を再利用します。 要するにReact.memoでコンポーネントを囲んだら、渡された引数に変化があるかどうかチェックして変化がある場合のみ再レンダリングされる機能が追加される。 結論 なんでもかんでもuseCallback使えばおkというわけではない 既に上記のCountButtonの例でuseCallbackだけ使ったら子コンポーネントの再レンダリングを防げないことを学びました。useCallbackの目的である「不必要なレンダーを避ける」ができてないのによい使い方のはずがないでしょう。 それに関数宣言はコストが安い処理なので、わざわざuseCallbackまで使って防げるべきものではない。 useCallbackを使うことにもコストがかかる。useCallbackというHookを読み込むし、第2引数で配列の宣言もするし、レンダリングの度にuseCallbackが動きます。useCallbackを使ったほうが得な時もあるが、場合によっては逆に余計なメモリを食う時だってある。 「When to useMemo and useCallback」の著者であるKent C. Doddsはこう言いました。 MOST OF THE TIME YOU SHOULD NOT BOTHER OPTIMIZING UNNECESSARY RERENDERS. React is VERY fast and there are so many things I can think of for you to do with your time that would be better than optimizing things like this. ほとんどの場合、不要なレンダリングの最適化は気にしなくていいです。Reactは非常に速いし、このようなものを最適化するよりも、他に時間を割いてやるべきことはたくさんあります そして今まで話した最適化の必要性も極稀と言ってます。彼がPayPalで働いた3年間、そしてそれよりも長いReact歴の間もそいういう最適化が必要な瞬間はなかったようです。 要するにuseCallbackを使うべき瞬間はそんなに多くないということになる。それなのに必要でもないHookを「とりあえず入れとこう」という気持ちで使うことはやめたほうがいい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

結局useMemoはいつ使えばいいの? 僕の決定版

皆さんこんにちは。筆者の以前の記事では、ReactのuseMemoを無駄に使うことによるレンダリング速度のオーバーヘッドがどれくらいかをベンチマークによって示しました。 それによれば、スマートフォンを想定したとしても、useMemoだけで描画に目に見える影響を与える(16msくらいの遅延を発生させる)には万のオーダーのuseMemoが必要なことが分かります。 速度ではなくuseMemoを使うことによるメモリ消費量の増加を気にする声も聞かれましたが、すみませんが筆者はそこまでメモリクリティカルなアプリをReactで書いたことがなく知見に乏しいため、今回はこの記事の対象外となります。 この結果が出たことでuseMemoをいつ使うのかなどという議論には終止符が打たれたかと思いきや、上記の記事の感想などを見ているとまだ喧々囂々です。 そこで、この記事では筆者の考えを皆さんに共有し、いよいよもってこの議論を終わりにしたいと思います。 結論は、今いる関数の外を見に行くな。そのuseMemoが今または将来に役に立つ可能性が1%でもあるなら使えです。 前提知識 useMemoというのは結局レンダリングパフォーマンスの最適化のためのAPIであることが知られています。 そこでまず前提知識として、useMemoがどのようにしてパフォーマンス最適化に寄与するのか復習しましょう。 useMemoは、以前行われた計算の結果をキャッシュし、useMemoが属するコンポーネントが再レンダリングしても、useMemoの第2引数(deps)が更新されていない場合は計算をスキップして以前の結果を使うという機能を持ちます。そこから考えると、重い計算の実行をスキップできることが恩恵に思えます。 const result = useMemo(() => { return すごく重い計算(計算に必要ななんらかのデータ); }, [計算に必要ななんらかのデータ]); しかし、実は本質はそこにはありません。なぜなら、そこまで重い計算をすることはあまり無いからです。筆者個人的には中で関数呼び出しが何個か行われていたりO(N)の処理があったりすれば気持ち的にuseMemoで囲みたくなりますが、正直気休めでありその程度でパフォーマンスが大きく変わるものではありません。本当にレンダリングに影響を与えるレベルで重い計算なら、useMemoの中でやらないでWeb Workerなどを使ったほうが良いでしょう。 では、日々の開発の中で受けられるuseMemoの恩恵は何でしょうか。それは、React.memoと組み合わせてコンポーネントのレンダリングを削減することです。具体的に言えば、同じ中身のオブジェクトを毎回新たに生成しないことがuseMemoの大きな役割です。 // nameとageが同じだったとしても毎回新しいオブジェクトになる const user1 = { name, age }; // nameとageが変わらない限り同じオブジェクトが使い回される const user2 = useMemo(() => ({ name, age }), [name, age]); React.memoで囲まれたコンポーネントのデフォルトの挙動では、「再レンダリング時に、渡されるpropsが全て以前と同じだったらレンダリングを省略する」というものです。Reactを使用する際はレンダリングの実行時間のうちReactランタイムが結構な割合を占めますから、React.memoを用いてレンダリングを省略することは目に見える効果があります。そのため「Reactアプリのパフォーマンスチューニング」というと基本的にはReact.memoでコンポーネントを囲ってレンダリングを減らしていくことおよびそのために必要な諸々の修正を指します。 例えば、上で生成したuser1やuser2を受け取る次のようなコンポーネントを考えましょう。 const ShowUser: React.VFC<{ user: User }> = React.memo(({ user }) => { return ( <div> <p>{user.name} ({user.age})</p> </div> ); }); このコンポーネントにuser1を渡していた場合、せっかくのReact.memoの効果が発揮されません。userに渡されるオブジェクトが毎回別物になるのでReact.memoは別のデータが来たと判断してShowUserを再レンダリングしてしまうからです。user2を渡した場合は、中身が変わらない限り同じオブジェクトを渡しているのでShowUserの再レンダリングが行われません。 実際にはShowUserは小さいコンポーネントなのでReact.memoを使ってもパフォーマンスへの寄与が少ないでしょう。実際のパフォーマンスチューニングではもっと大きなコンポーネントを狙ってReact.memoを使っていくことになります。 以上のことを実際に体験できるCodeSandboxを用意してみました。具体的なコードの説明は省略しますが、この例ではuseTimeによってAppが秒間10回再レンダリングされるようになっています。デフォルトではuserがuseMemoによって最適化されているので、ShowUserはuserの中身が変わったときだけ再レンダリングされます。一方、この最適化を外すと(useMemoがuseUserFormの中にあります)、中身が変わっていないのにShowUserも秒間10回再レンダリングされます(再レンダリングされるとconsole.logが実行されるようになっていて確かめられます)。 このように、useMemoは、React.memoを使っているコンポーネントに渡されるオブジェクトが変わるのを最小限にするという目的で使うのがもっとも主流のユースケースです。ちなみに、useCallbackに関しても「オブジェクト」を「関数」と読み替えれば全く同じことが成り立ちます。 useMemoをいつ使うのか ここからが本題です。以上のことから、無駄なuseMemoを配置しないことにこだわるのであれば、「React.memoを使ったコンポーネントに渡されるオブジェクトが生成されるところにuseMemoを差し込む」という戦略を取ることになります。 しかし、そのようなやり方は筆者としてはおすすめしません。なぜなら、開発効率が悪いし設計(カプセル化)の観点からも良くないからです。 例えば、上のCodeSandboxのアプリケーションで最初は最適化が何もされておらず、React.memoによる最適化を行う場合を想定しましょう。まずShowDataをReact.memoで囲みますが、それだけだと再レンダリングが減らないため、userが再生成されるのを防ぐ必要性があることに気づきます。そして、userの生成元がuseUserFormであることを突き止め、そこにuseMemoを差し込んで解決という流れになるでしょう。 このようなやり方は次の理由から良くありません。 開発効率が悪い(ひとつReact.memoを差し込むためにプロジェクトの中をかけずり回る必要がある) 設計が悪い(useUserFormの内部実装がuseUserFormを使う側の都合で決められている) 筆者のおすすめのやり方は、「useUserFormを書く時点で何も考えずにuseMemoを書いておく」ことです。確かに、そのuseMemoが将来的に役に立つか(React.memoを使うコンポーネントの再レンダリングを削減するのに活用されるかどうか)は不明であり、役に立たない場合は無駄なuseMemoとなります。しかし、もし将来的に役に立った場合はそのときの開発効率に寄与することになります。このような、言わば“予防的なuseMemo”を筆者としては推奨します。ランタイムの0.1マイクロ秒程度のオーバーヘッドを対価に将来の開発効率悪化を防止できるのなら万々歳ではないでしょうか。もちろんランタイムに債務を押し付けすぎるのは良くありませんが、そもそもReactなどを使っている時点である意味ランタイムにかなりの部分を押し付けていますから今さらです。 また、そもそも実際に役に立つかどうかに関わらず、カスタムフックの外に渡すオブジェクトを適切にuseMemoで囲うことはお行儀のいいカスタムフックであるために必要なことだと考えています。そもそもReactからデフォルトで提供されるフックも、最大限のお行儀の良さで提供されています。例えば、useStateやuseReducerから得られるステート更新関数は常に同じオブジェクトです。 極端な話、ライブラリとしてカスタムフックを提供する場合、もしそのライブラリの中のuseMemoが足りないせいでパフォーマンス改善ができないとなったらもうお手上げです。ですから、ライブラリならば予防的に最大限useMemoを使用しておくことは必須です。これがお行儀の良さということですね。そしてそれは、もしあなたが「再利用可能性」とか「適切なインターフェース」といったことを考えながら普段のコードを書いているならば、プロジェクト内のカスタムフック等にも当てはまるはずです。再利用可能なコードは、それを使う側の個々の都合でいちいち内部実装を変えるものではありません(必要に応じた機能追加などはあり得ますが、それはインターフェースの変更でありもはや内部実装の変更ではありません)。 ここではuseUserFormの例に合わせてカスタムフックの話をしましたが、カスタムフックではなくコンポーネント内で直に使うuseMemoについても同様です。そのようなuseMemoによって作られたオブジェクトは子コンポーネントに渡されることになります。あとからuseMemoを加えることは「子コンポーネントの都合で親コンポーネントを変える」ことになりますから、やはり設計等の観点から良くありません。 さすがに使わない場合はいつか 以上のように、筆者としては今役に立たないとしても、将来的に役に立ちそうであればどんどんuseMemoを書くことを推奨しています。では逆に、useMemoを書かなくてもいい場合はどんな場合でしょうか。それは、将来的に役に立たないことが確定的に明らかな場合です。 例えば、次のようなuseMemoはさすがに余計でしょう。 // このuseMemoは余計 const ageNum = useMemo(() => Number(age) || 0, [age]); これはそもそもNumber(age) || 0が全然重くない計算なのでキャッシュする意味が無いこと、そしてオブジェクトを生成する計算ではないのでそちらの利点もありません。明らかに無駄です。 また、オブジェクトを生成する計算であっても次のような場合は筆者はuseMemoを使いません。 // このuseMemoも余計 const style = useMemo(() => ({ display: 'flex', flexFlow: 'row nowrap', }), []); // ... return <div style={style}>...</div>; この場合、オブジェクトひとつ生成するという計算自体のオーバーヘッドは低いのでそれをuseMemoで囲もうとは思いません。また、今回styleオブジェクトはdivに渡されることが明らかです。実は、divのようなネイティブのHTML要素に対しては毎回別のオブジェクトを渡してもパフォーマンス上の問題がありません。そのため、ここではuseMemoを使わないことを選択しています。 注意として、もし以下のコードならば筆者はuseMemoを使用します。 // このuseMemoは余計ではない! const style = useMemo(() => ({ display: 'flex', flexFlow: 'row nowrap', }), []); // ... return <MyComponent style={style}>...</MyComponent>; この場合はオブジェクトが渡す先がdivではなく別のコンポーネントです。この場合は別のオブジェクトを生成しないことに価値がある可能性があるので、お行儀よくオブジェクトの再生成を回避します。 ポイントは、MyComponentの実装をわざわざ確かめに行かないということです。今必要なかったとしても将来的にReact.memoが追加されるかもしれないし、MyComponentからさらに別のコンポーネントにstyleがバケツリレーされるように変わるかもしれないし、将来的にuseMemoが必要とされる可能性を否めないからです。useMemoを予防的に書くことは将来への投資ですから、今わざわざ時間をかけるべきではありません。 このことから、useMemoを書くかどうかは今いる関数の中だけ見て決める(関数の外をわざわざ見に行かない)という考え方につながります。関数というのはひとつの開発単位であり、関数を実装するときにその関数全体に視野を広げるのは普通のことですが、関数の外まで視野を広げるべきではありません。それでは関数を分けた意味がかなり薄れるからです。 以上のように、将来の開発効率に投資するための予防的なuseMemoというのが筆者の考え方です。 まとめ useMemoは、今書いている関数の中だけを見て明らかに無駄でなければどんどん書きましょう。今書いている関数の外のことまで考慮し始めるのは開発効率が悪いし設計も悪いです。もし書いたuseMemoが将来的に役に立てば大きなメリットになりますが、役に立たなかったとしてもそのコスト(オーバーヘッド)はわずかです。 余談: タイトルに「僕の」と入っているのを見ると一部の箱庭諸島にあった「僕の引越し」コマンドを思い出しますよね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】react-beautiful-dndの記述方法

はじめに  本記事は、プログラミング初学者が、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。  そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。  間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。 react-beautiful-dndの記述方法 Reactで、react-beautiful-dndを使用する際には、以下のように記述します。 App.jsx import {useState} from 'react'; import {DragDropContext, Draggable, Droppable} from 'react-beautiful-dnd'; import './App.css'; function App() { const [items] = useState([ {id: 0, text: "item0"}, {id: 1, text: "item1"}, {id: 2, text: "item2"}, ]) const onDragEnd = (result) => { const remove = items.splice(result.source.index, 1); console.log(remove); items.splice(result.destination.index, 0, remove[0]); }; return ( <div className="dragDropArea"> <DragDropContext onDragEnd={onDragEnd}> <Droppable droppableId='doroppable'> {(provided) => ( <div {...provided.droppableProps} ref={provided.innerRef}> {items.map((item, index) => ( <Draggable draggableId={item.text} index={index} key={item.id}> {(provided) => <div className='item' ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps} > {item.text} </div> } </Draggable> ))} {provided.placeholder} </div> )} </Droppable> </DragDropContext> </div> ); } export default App;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む