yarnで古いバージョンのパッケージをインストールする方法メモ
記事の移行について
本記事の詳細は、こちらに移行しました。
はじめに
Reactのreact-router-domがメジャーバージョンアップした(バージョン6)とのことで、バージョン6の記法を勉強できていないためバージョン5をインストールしたい。
yarnを使って実施する方法のメモ。
結論
バージョンを指定してパッケージインストールする方法。
react-router-domのバージョン5、かつマイナーバージョンは最新のものをインストールしたい場合
$ yarn add. react-router-dom@5.x
react-router-domのバージョン5.2をインストールしたい場合
$ yarn add. react-router-dom@5.2
React × Typescriptでコンポーネントを書く時のテンプレートメモ
はじめに
今回の記事の目的
React × TypeScriptでコンポーネントを追加するときに毎回迷うので、基本となる要素をメモ。
基本のテンプレート
MyComponent.tsx
※ファイル名は大文字から開始
import { memo, ReactNode, VFC } from "react"; type Props = { children: ReactNode; //childrenは「ReactNode」 onClick: () => void; // 関数のprops disabled?: boolean; // 任意のpropsには ? をつける // ・・・その他propsの引数の型 }; export const MyComponent: VFC<Props> = memo((props) => { const { children, onClick, disabeld = false, …その他のprops… } = props; // ・・・処理 return ( // ・・・返却値 ); });
VFC
関数型コンポーネントであることを表す。
任意のprops項目
任意のpropsを指定する場合は、分割代入する際にdisabled = false
といった形で初期値を指定する。
memo
について
memo
関数で囲むことで「propsに変更がない限り、親コンポーネントが再レンダリングされてもこのコンポーネントは再レンダリングしない」ようにできる。
必要に応じて利用する。
おわりに
コンポーネントを作成するときに必要な要素をメモした。
ReactでCSSを適用する方法のメモ
はじめに
今回の記事の目的
ReactでCSSを当てる方法はいくつかある。覚えておけばよさそうなものだけ抜粋してメモ。
- はじめに
- classNameでクラスを設定
- インラインスタイル
- styled-components
- コンポーネントライブラリ(chakra-uiやMUI)を利用
- その他の方法
- 表示内容
- おわりに
- 参考文献
classNameでクラスを設定
ソースコード
App.tsx
function App() { return ( <> {/* 方法1:classNameでクラスを設定 */} <div className="red-area"> <h1>方法1</h1> <p> classNameというpropsにクラス名を渡す。 index.tsxで呼び出しているindex.clasNameというpropsにクラス名を渡す。 index.cssに通常のCSSを記載する。 </p> </div> </> ); } export default App;
index.css
.red-area { background-color: red; color: white; }
インラインスタイル
ソースコード
App.tsx
const style = { container: { backgroundColor: "red", color: "white" } }; function App() { return ( <> {/* 方法2:インラインスタイル */} <div style={style.container}> <h1>方法2</h1> <p> インラインスタイル:styleというpropsに、オブジェクトを渡す。 オブジェクトの型はReact.CSSProperties。 ここではstyleオブジェクトの中に、 要素ごとのReact.CSSPropertiesオブジェクトを設定している。 </p> </div> </> ); } export default App;
styled-components
インストール
$ yarn add styled-components $ yarn add @types/styled-components
@types/styled-components
はtypescriptの場合に必要。
ソースコード
styled.div``
で囲んだ中にはCSSをそのまま書ける。:hover
なども書ける。
import styled from "styled-components"; function App() { return ( <> {/* 方法3:styled-componentsモジュールを利用 */} <SContainer> <h1>方法3</h1> <p> styled-componentsをインポートする。 関数の外で、例えばdivをベースに独自のスタイルをつけたコンポーネント SContainerを用意する。 </p> </SContainer> </> ); } // SContainerの定義 const SContainer = styled.div` background-color: red; color: white; `; export default App;
div
等HTMLのタグを装飾する場合は
const SContainer = styled.div`
とするが、自作のコンポーネントを装飾する際は
const SContainer2 = styled(SContainer)`
のように記載する。
コンポーネントライブラリ(chakra-uiやMUI)を利用
今回はchakra-uiを利用する。
最も有名なのはMaterial UIらしい。 mui.com
インストール
$ yarn add @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4
ソースコード
import { ChakraProvider, Box, Text, Heading } from "@chakra-ui/react"; function App() { return ( <> <ChakraProvider> <Box bg="red" color="white"> <Heading>方法4</Heading> <Text> コンポーネントライブラリのchakra-uiを利用。 divタグの代わりにBox、pタグの代わりにTextなど 便利に使えるコンポーネントがそろっている。 </Text> </Box> </ChakraProvider> </> ); } export default App;
<ChakraProvider>
タグで囲む必要があるが、親コンポーネントで囲んでおけば子コンポーネントでもchakra-uiが使える。
その他の方法
これ以外にも方法は様々あるらしい。
- CSS Modulesを利用する方法。有名なモジュールであるnode-sassは非推奨になっている模様。
- StyledJsxというモジュールを利用する方法。
- emotionというモジュールを利用する方法。
表示内容
おわりに
方法3と4をうまく使えれば良さそう。
参考文献
【React】レンダリングを制御するための関連知識メモ
はじめに
Reactではレンダリングの動作を理解することがとても重要で、レンダリングを制御するための機能が様々用意されている。use~系が多くて難しいので、基本的なところをメモしておく。対象は以下4つとする。
useEffect
副作用を制御するuseEffect。
再レンダリングに関わる関数・メソッド
以下、メモ化三兄弟。
- memo():コンポーネントのメモ化
- useCallback():関数のメモ化
- useMemo():変数(関数の処理結果)のメモ化
Reactで再レンダリングが行われる条件
そもそもReactでは、以下の条件で再レンダリングが行われる。
useEffect()
useEffectは以下2つの目的で使うものらしい。
- ある処理を、特定の値が変化した時のみ動作させることができる
- レンダリングが完了した後に処理を実行させることができる
公式ページには以下のように記載がある。
副作用を有する可能性のある命令型のコードを受け付けます。
DOM の書き換え、データの購読、タイマー、ロギング、あるいはその他の副作用を、関数コンポーネントの本体(React のレンダーフェーズ)で書くことはできません。それを行うと UI にまつわるややこしいバグや非整合性を引き起こします。
代わりに useEffect を使ってください。useEffect に渡された関数はレンダーの結果が画面に反映された後に動作します。副作用とは React の純粋に関数的な世界から命令型の世界への避難ハッチであると考えてください。
デフォルトでは副作用関数はレンダーが終了した後に毎回動作しますが、特定の値が変化した時のみ動作させるようにすることもできます。
副作用を含む処理はuseEffectの中に書くように…と言っている。(難しい)
副作用とは、alert
や、APIでのデータ取得や、console.log
などがある。
使い方
useEffect ( () => {処理}, [依存配列] )
依存配列にstateを設定すると、そのstateが更新されたときのみ第一引数の処理を実行する。依存配列に[]
を設定した場合は、初期レンダリング完了時のみ処理を実行する。
利用例
特定の値が変化した時のみ動作させる目的での使い方を記載しておく。 関心を分離するために複数の副作用を使うという例が公式ページにも記載されている。
たとえば値をカウントアップしていって、値が10で割り切れる数になった際にカウント2(10の位をカウントアップするプログラムを作ってみる。
App.tsx
import { useEffect, useState } from "react"; export const App = () => { const [count1, setCount1] = useState(0); const [count2, setCount2] = useState(0); // count1が更新された場合のみ以下の処理を実行する useEffect(() => { if (count1 > 0 && count1 % 10 === 0) { setCount2((count2) => count2 + 1); } }, [count1]); const onClickCountUp = (e: React.MouseEvent<HTMLButtonElement>) => { setCount1((count1) => count1 + 1); }; return ( <> <button onClick={onClickCountUp}>カウントアップ</button> <p>{`カウント:${count1} カウントの10の位:${count2}`}</p> </> ); };
もしuseEffectを使わなかった場合、以下のエラーが出ることになる。
Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
count1が10になった際、count2がカウントアップされて1になる。count2が変更されたので再レンダリングされる。count1は10なのでまたcount2がカウントアップされる。count2が変更されたので・・・と、count2のカウントアップが繰り返されるためである。
memo()
コンポーネントをメモ化してくれる関数。コンポーネントに対して設定する。親コンポーネントが再レンダリングされても、propsに変更がない限り再レンダリングされないようにする。
使い方
子コンポーネント側の関数をmemo()
で囲む。(記載例はtype-script。)
Child.tsx
// インポートが必要 import { memo, VFC } from "react"; type Props = { text: string; }; // 関数全体をmemoで囲む export const Child: VFC<Props> = memo((props) => { const { text } = props; return ( <div>{text}</div> ); });
このときChildコンポーネントは、呼び出す側の親コンポーネントが再レンダリングされたとしても、propsであるtext
が更新されない限りは再レンダリングされなくなる。
useCallback()
第一引数に渡している関数をメモ化してくれる。
第二引数で、関数を再定義する条件を指定する(useEffect
と同様)。
どんなときに使うか
基本的には、子コンポーネントのpropsとして関数を渡している場合に利用する。
memo()
で子コンポーネントをmemo化しても、propsに関数がある際、親コンポーネントが再レンダリングされた際に関数が再定義されることで、結局propsが変更された扱いとなり、子コンポーネントが再レンダリングされてしまう。
そうなるのを防ぐために、子コンポーネントにpropsで渡している関数をuseCallback()
でメモ化しておく必要がある。
逆に、子コンポーネントをmemo化していないと意味が無いので注意。
利用例
親コンポーネント側
App.tsx
import { useCallback, useState } from "react"; import { Child} from "./Child"; export const App = () => { const [text, setText] = useState(""); const [count, setCount] = useState(100); const onClickInit = useCallback(() => setCount(0), []); return ( <> <p>{`カウント:${count}`}</p> <Child onClick={onClickInit} /> </> ); };
子コンポーネント側
Child.tsx
import { memo, VFC } from "react"; type Props = { onClick: () => void; }; // memo化しているのでonClickが再定義されなければ再レンダリングされない export const Child: VFC<Props> = memo((props) => { const { onClick } = props; return ( <> <button onClick={onClick}>カウント初期化</button> </> ); });
useMemo()
変数をメモ化してくれる関数。 実践で利用したことはまだ無い。
使い方
変数の導出処理が重い場合、再レンダリングするたびに計算すると大変なので、初回レンダリング時にのみ計算するよう設定する。
第二引数の設定は、useEffectやuseCallbackと同じルール。
const value = useMemo(() => (1+2+3+4+…重い計算処理…) , []);
実際に使ってみた例は以下となる。
import { useMemo, useState } from "react"; export const App = () => { const calc = () => { let num = 0; for (let i = 1; i < 100000000; i++) { num += i; } console.log("重い計算処理calc()を実行"); return num; }; // 変数valueの計算が重い場合 const value = useMemo(() => calc(), []); // stateを更新するボタン const [state, setState] = useState(0); const onClickState = () => setState((state) => state + 1); return ( <> <button onClick={onClickState}>stateを更新するボタン</button> <p>{`State:${state} value:${value}`}</p> </> ); };
重要なのは
const value = useMemo(() => calc(), []);
の部分で、このように書くことでvalue
の計算が初回レンダリング時のみで済む。
普通に
const value = calc();
と書くと、ボタンを押してstate
を変更するたびにcalc()
が実行されてしまうことがコンソールログから確認できる。
おわりに
ひとまずメモ化三兄弟を整理できた。useEffectは奥が深く理解しきれていないので、理解し次第追記する。
AWS Amplify がビルドに失敗したときの原因をメモ
はじめに
AWS AmplifyでReactアプリを作成しようとしたところ、ビルド実行時にエラーとなったので原因をメモ。
前提
エラーの内容
エラーは以下。リポジトリ名、アプリ名はay-s-○○○○
で伏せます。
2021-11-07T03:30:28.392Z [INFO]: # Cloning repository: https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/ay-s-○○○○ 2021-11-07T03:30:28.847Z [INFO]: Cloning into 'ay-s-○○○○'... 2021-11-07T03:30:38.226Z [INFO]: fatal: unable to access 'https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/ay-s-○○○○/': The requested URL returned error: 403 2021-11-07T03:30:38.229Z [ERROR]: !!! CustomerError: Unable to clone repository due to user error code: 128 2021-11-07T03:30:38.230Z [INFO]: # Starting environment caching... 2021-11-07T03:30:38.290Z [INFO]: # Environment caching completed Terminating logging...
要するに、
fatal: unable to access 'https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/ay-s-○○○○/': The requested URL returned error: 403
CodeCommitのリポジトリからcloneしようと思ったけどアクセスができません!!ということのようです。
エラーの原因
Amplify側に設定したサービスロールの内容が間違っていた。
以下サイトに同じ事例があった。
Unable to clone repo (403) at Build step · Issue #572 · aws-amplify/amplify-console · GitHub
サービスロールの設定箇所
Amplifyで「new app」を作成する画面で、サービスロールを選択する場所がある。
おそらく、ここで「new service role」のほうを選んでいれば問題ないと思うが、既存のロールを選んでしまったと思われる。
Amplifyに設定されているサービスロールの確認
「アプリの設定」→「全般」で確認できる。
サービスロールに適用されているポリシーの確認
IAMのコンソールからロールに設定されたポリシーの中身を確認すると、「Resource」に記載されているCodeCommitのARN名が間違っていた。(既に削除した、昔のリポジトリのものが入っていた)。
ここを修正した。
IAMポリシーに設定すべきCodeCommirのARN名確認方法
ARN名は「CodeCommit」の該当リポジトリ→「設定」で確認できる。
おわりに
IAMポリシーを修正し、無事デプロイが完了した。
関連記事
CodeCommitについて詳細
CodeCommitの使い方を記載した記事。 tomiko0404.hatenablog.com
重要だけど忘れそうなJavaScriptの記法メモ
はじめに
Reactを勉強する中でJavaScriptの記法を利用している。
その中でよく出てきた、超基本の重要なJavaScript記法をメモしておく。今後、何か月か勉強をお休みすることがあると忘れそうなため。
前提
以下の例に記載してるものは、参考環境の説明章で述べるindex.js
に記載しているもの。
アロー関数
こんな文法で無名関数がつくれる。
( 引数 ) => { 処理 }
非常に重要かつ、私自身が罠にはまったので、別記事にまとめている。
テンプレート文字列
文字列と変数を結合して出力したいときの書き方。
古い書き方だと、文字列はダブルクォーテーション""
でくくり、変数はそのまま記載したものを+
で結合する。
これだと読みにくいのでテンプレート文字列という書き方がある。
- 全体を
``
(バッククォート)で囲む。 - 変数を使いたいときは
${変数名}
と記載する。
index.js
const math = 90; const japanese = 60; const english = 85; // 古い書き方 console.log( "算数:" + math + "点、国語:" + japanese + "点、英語:" + english + "点です。平均は" + (math + japanese + english) / 3 + "点です。" ); // テンプレート文字列を利用した書き方 console.log( `算数:${math}点、国語:${japanese}点、英語:${english}点です。平均は${(math + japanese + english) / 3}点です。` );
出力結果は以下。
算数:90点、国語:60点、英語:85点です。平均は78.33333333333333点です。 算数:90点、国語:60点、英語:85点です。平均は78.33333333333333点です。
分割代入
オブジェクトや配列の中身を変数に代入する方法。
オブジェクトの場合
オブジェクトの項目名に合わせた変数名で取得できる。
const point = { math: 90, japanese: 60, english: 85, science: 95, social: 55, }; // オブジェクトの分割代入 const {math, japanese, english} = point; console.log( `(point)算数:${math}点、国語:${japanese}点、英語:${english}点です。` );
配列の場合
配列の場合は以下の通り、順番通りに分割代入できる。
代入の際にはオブジェクトの場合と異なり[]
で囲む。
また、配列の前半に変数に代入する必要がないレコードがある場合は、以下のように変数名を入れなくてもよさそう。
const pointArray = [90, 60, 85, 95, 55]; // 配列の分割代入 const [, , , science, social] = pointArray; console.log(`(pointArray)理科:${science}点、社会:${social}点です。`);
出力結果はオブジェクト分割代入のコードと合わせて以下の通り。
(point)算数:90点、国語:60点、英語:85点です。 (pointArray)理科:95点、社会:55点です。
上記の例のように使うシーンがあるかは不明だが、Reactでは
[num, setNum]=useState(0);
というように、公式の関数から値や関数を取り出すときにたくさん利用する。
スプレッド構文
配列の中身を1個ずつ取り出す構文。
...[配列名]
正確な記載ではないのだが、array1 = [1,2,3,4,5];
に対して、...array1
と書くと、1,2,3,4,5
という扱いになる。とざっくり思っている。
配列の一番後ろに要素を足す(よく使うやり方)
let messages = [ "こんにちは", "元気ですか", "はい元気です", "テストどうでしたか", ]; messages.forEach((i) => console.log(i)); console.log("------------------------") // スプレッド構文でmessagesを展開してから結合 messages = [...messages, "まあまあです"]; messages.forEach((i) => console.log(i));
出力結果は以下の通り。
こんにちは 元気ですか はい元気です テストどうでしたか ------------------------ こんにちは 元気ですか はい元気です テストどうでしたか まあまあです
オブジェクトのの一番後ろに要素を足す
オブジェクトでも同様に要素を足すことができる。
let point = { math: 90, japanese: 60, english: 85, science: 95, social: 55, }; console.log(point); // スプレッド構文でオブジェクトの要素を追加 point = { ...point, music: 100 }; console.log(point);
出力結果は以下の通り。
関数の引数にも利用できる
あまり利用シーンは無いように感じられるが、配列から1要素ずつ取り出すので以下のようなこともできる。
const pointArray = [90, 60, 85, 95, 55]; const pointAvg = (p1, p2, p3, p4, p5) => (p1 + p2 + p3 + p4 + p5) / 5; // スプレッド構文で90, 60, 85, 95, 55を引数に入れる console.log(`5教科の平均点は${pointAvg(...pointArray)}点です。`);
出力結果は以下の通り。
5教科の平均点は77点です。
map構文
配列の1個1個の要素に対して処理を記載できる。再頻出でかなり重要。
mapの引数には関数を入れる。この関数は、第一引数に配列の各要素、第二引数に配列のインデックスが設定された関数である。
let messages = [ "こんにちは", "元気ですか", "はい元気です", "テストどうでしたか", ]; // map関数でmessagesの各要素に対して処理 messages.map((data, index) => { console.log(index + ":" + data); });
ここで利用しているアロー関数では、先ほど述べた罠にはまらないように注意。
出力結果は以下の通り。
0:こんにちは 1:元気ですか 2:はい元気です 3:テストどうでしたか
参考までに、以下の記事ではHTMLの<li>
要素に各要素を入れて返却したい箇所で利用している。
<ul> {messageList.map((data, index) => { return <li key={index}>{data}</li>; })} </ul>
参考:環境の説明
プロジェクトフォルダを作成し、その中に
- index.html
- index.js
の2ファイルを作成する。
index.htmlをブラウザで表示し、ボディ部でindex.jsを呼び出す。
index.html
<!DOCTYPE html> <html lang="jp"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Javascriptのメモ</title> </head> <body> <!-- ★★bodyタグの中にscriptタグを記載 ★★--> <script type="text/javascript" src="index.js"></script> </body> </html>
おわりに
Reactの勉強を進めることで、JavaScriptの文法はだいぶ頭に入ってきた。 忘れたときにはこの記事が参考になると良いと思います。
【React/ useState】setState関数の書き方が下手で大量レンダリングが起こった話
はじめに
Reactを利用してアプリを作っていたところ、予期せぬ大量レンダリングが発生して画面描画が遅くなってしまった。
いろいろ試した結果、useStateのset関数の使い方が悪いことがわかったのでメモ。
ひとこと結論
setState関数の引数を「関数にする」
自分自身のstateを使う際は、setState関数の引数には「値」ではなく「関数」を入れる。 そして
全体を「useEffectで囲む」
実施内容
背景
Reactを利用して簡易チャットアプリを作成した。詳細は以下の記事を参照。
作成したチャットアプリ tomiko0404.hatenablog.com
その中で、以下のsetMessageList部分の処理で大量のレンダリングが走っていた。
// メッセージリスト(全員が送ったメッセージの一覧)をStateとして定義 const [messageList, setMessageList] = useState<string[]>([]); // サーバから、誰かからのメッセージが送信されたときの処理 // messageListの一番後ろに、サーバから受け取ったメッセージを追加する。 socket.on("chat", (msg) => { setMessageList([...messageList, msg]); console.log("setMessageListを実行:" + messageList); });
処理を簡単に説明すると、
- 画面側で、メッセージ一覧画面に表示するためのメッセージリスト
messageList
をStateとして管理している。 - 誰かがメッセージを送信したら、サーバがそれを受け取り、全員の画面に向けてメッセージを発信する。
- メッセージを受け取った画面側は、
messageList
にの一番後ろに今受け取ったメッセージを追加することで、画面に表示する。
という内容である。
失敗① 単純にsetMessageListを実行した場合
背景に記載したコードをそのまま実行した場合。
「1回目」「2回目」・・・「5回目」までメッセージを送信したところ、5回目にはかなり描画が遅くなっていた。(2秒ほど)
コンソールログを見てみる。わかりやすいように、メッセージを送信するたびに1行の空白行を入れている。 5回目のメッセージを送信した際は、2,098回ものsetMessageList処理が走っていることがわかる。
元々のイメージでは、例えば4回目のメッセージを送ったとき、messageList
の値は[1回目, 2回目, 3回目]
から [1回目, 2回目, 3回目, 4回目]
に変更されると思っていた。しかし、ログからは[1回目]
や[1回目, 2回目, 4回目]
などすべてのパターン一通り通ってから[1回目, 2回目, 3回目, 4回目]
に落ち着いているように見える…。
注)ちなみに表示されているmessageList
とログのmessageList
が合っていない(画面では5回目まで表示されているのにログには4回目までしか入っていない)のは、console.log
の位置の問題だと思われるが、詳細は不明。
失敗② useEffectを利用した場合
Reactでは、再レンダリングする条件として、何らかのステートが更新された場合という条件が存在する。
参考資料: 【React】再レンダリングの仕組みと最適化
今回、messageList
以外にもstateを用意していたので、それらの値が変化するたびに実行されてしまうのは問題がありそう。
ということで、useEffect()
を利用することにした。
useEffect(() => { socket.on("chat", (msg) => { setMessageList([...messageList, msg]); console.log("setMessageListを実行:" + messageList); }); }, []);
結果、以下のように最新のメッセージしか表示されなくなってしまった。
この理由は明白で、useEffect
の第二引数を空配列[]
にしていることである。
第二引数を空配列にすると、この関数は初回レンダリング時にしか実行されなくなる。
結果、サーバからメッセージを受け取った際に行う処理(関数)の定義を初回レンダリング時にしか行なわない。つまりこの関数は、「空配列(messageListの初期値)の後ろに受け取ったメッセージを付け加える」という関数となる。
失敗③ useEffectの第二引数にmessageListを加えた場合
失敗②では、関数が定義され直さないのがいけなかったということで、messageListの値が変更されたときだけ関数を定義しなおしてもらおうということで、useEffect
の第二引数にmessageList
を入れてみた。
useEffect(() => { socket.on("chat", (msg) => { setMessageList([...messageList, msg]); console.log("setMessageListを実行:" + messageList); }); }, [messageList]);
結果、失敗①と全く同じ結果になった。。。
成功例
以下の記事を見つけた。
状態更新関数の引数に与えるものは、プリミティブに限りません。関数を与えることで、prev state を参照出来ます。これにより、初期レンダリング時のみに handleClick関数定義を抑止し、正しく動かすことが出来ます。 useState で生成された関数は、同時に生成された状態を参照しない方が良さそう、という話でした。
つまり、setMessageList()
に値ではなく関数を与えれば、定義自体は初回レンダリング時にしか行なわれず(useEffectの効果)、さらに引数には最新の値を使ってくれる(関数を与えたことの効果)らしい。
setMssageList
の定義を書き換える。
// setMessageList([...messageList, msg]); // 変更前 setMessageList((messageList) => [...messageList, msg]); // 変更後
全体はこのようになる。useEffect
の第二引数は、[]
(空配列)に戻す。
useEffect(() => { socket.on("chat", (msg) => { setMessageList((messageList) => [...messageList, msg]); console.log("setMessageListを実行:" + messageList); }); }, []);
結果として、画面描画速度が大幅に向上した。
コンソールログ上は、messageList
の値が初期値のままになってしまっているが、これはイベント時の処理の定義が初回レンダリング時にしかされていないためである。
わかりにくいので画面描画時にもログを出力してみると、メッセージリストの更新前と更新後の値が出ているのみとなった。問題なさそう。
公式ドキュメントの記載
よく見ると公式ドキュメントにも記載があった。
新しい state が前の state に基づいて計算される場合は、setState に関数を渡すことができます。この関数は前回の state の値を受け取り、更新された値を返します。以下は、setState の両方の形式を用いたカウンタコンポーネントの例です。
おわりに
参考文献が少なかったり古かったりでかなり苦しんだが、チャットアプリの根幹にかかわる部分だったので解決できてよかった。