Reactメモ
(Vite) ビルド時に相対パスにする
vite.config.ts(js) をいじる
code:ts
export default defineConfig({
base: "./" //<-- 追加
})
CSSクラスを書く
code:ts
<div className="bg-black">
class=...はエラー
</div>
CSSスタイルを書く
code:ts
<div style={{ backgroundColor:"black" }}>
属性名はキャメルケース(camelCase)
</div>
配列の数だけ繰り返しレンダリング
配列から「レンダリング用配列」を作成する方法
code:ts
let buttonInfoList = [
{ label: "1", value: "a" },
{ label: "2", value: "b" },
{ label: "3", value: "c" },
];
let buttonNodes = [];
for (const buttonInfo of buttonInfoList) {
buttonNodes.push(
<li>
<button data-value={buttonInfo.value}>
{buttonInfo.label}
</button>
</li>
);
}
...
return (
<ul>{buttonNodes}</ul>
);
レンダリング側がシンプルになり見通しが良い
レンダリングのみにしか使わない配列を新規に作ることに抵抗感がある
mapを使う方法
code:ts
let buttonInfoList = [
{ label: "1", value: "a" },
{ label: "2", value: "b" },
{ label: "3", value: "c" },
];
...
retrn (
<ul>
{buttonInfoList.map((buttonInfo) => ( //※{return ...}は省略でき、(...)でOK
<li>
<button data-value={buttonInfo.value}>
{buttonInfo.label}
</button>
</li>
))}
</ul>
);
余計な配列を増やさなくて済む。「レンダリング前準備」が必要なくなる。
配列が2次3次と増えていくと地獄になりそう…
1要素ごとに出力する関数を作る方法
code:ts
let buttonInfoList: ButtonInfo[] = [
{ label: "1", value: "a" },
{ label: "2", value: "b" },
{ label: "3", value: "c" },
];
...
return (
<ul>{buttonInfoList.map(buttonRender)}</ul>
);
}
...
function buttonRender(buttonInfo) {
return (
<li>
<button data-value={buttonInfo.value}>
{buttonInfo.label}
</button>
</li>
);
}
レンダリング側はシンプルになる。配列も作らなくて済む。関数は1行ごとに集中して処理を書ける。配列が入れ子になっても混乱が起きにくい。
新規関数を作るのが面倒。レンダリング部だけをみて何をしているのかが分かりづらい。処理が飛ぶので流れが追いづらい。
ステートの宣言(useState)
簡単にいうとステートとは「常に最新の値を表示に反映させておきたい変数」、すなわち「その値を変更すると再描画(render)がかかる変数」のことである。
React内部で値の更新を監視しなければならないため、更新には専用の関数を用意する。この関数以外の方法で値を更新することは許されない(単純な代入をしようとするとTypescriptでは(型が違うため)エラーが出ることが多いが、出ないこともあるので注意(後述))
code:ts
//countが変数名、setCountが変更用の関数。内部的にcountはもうnumber型ではない
//変更したいとき
setCount(1); //このタイミングで再描画が確定する
ひとつのコンポーネントが大量のステートを持つ、あるいは上位のコンポーネントが頻繁に更新されるステートを持つと再レンダーの回数が増え、効率的な表示が行えなくなる。ある程度の区切りでコンポーネントを分ける必要がある。
ステートの更新タイミング
以下のプログラムでcountの値としてlog出力されるのは、ボタンを押す前の値である
code:js
export default function Index () {
const onButtonClick = () => {
setCount(count + 1);
console.log(count);
}
return <button onClick={onButtonClick}>PUSH</button>
}
stateの値が更新されるのはreactによって変更が検出され、レンダーされたあとらしい。
なので以下のようにする。
code:js
const onButtonClick = () => {
let newCount = count + 1
setCount(newCount);
console.log(newCount);
}
ステートへの破壊的メソッド
破壊的メソッド(その関数自体が変数の内容を変更してしまう)は配列操作に比較的多くある。
push/pop/shift/unshift …配列の頭/末尾からひとつ取り出す/追加する。
splice ... 要素を削除する
sort/reverse ... 要素を並べ替える
これらをuseStateで定義したステートの配列にうっかり使ってしまうと、正しい状態でなくなってしまう。エラーも出ない。
なのでこれらを行う前に配列を複製し、操作を行ってから元に戻すという作業が必要。
code:ts
let newList = Array.from(list); //(シャロー)コピー
newList.push("d"); //破壊的メソッド
setList(newList);
マウント時/アンマウント時/レンダーのたび/最初の1回だけ実行したい
その1:マウント時/レンダー時に実行:useEffect(<関数>)
code:js
useEffect(() => {
//マウント時、および再レンダーごとに実行されます
somefunc();
});
その2:アンマウント時に実行:useEffect( () => {return <関数>} )
code:js
useEffect(() => {
return () => {
//アンマウント時に実行されます
anotherfunc();
}
});
その3:ステートに変更がなければ実行しない:useEffect(<関数>, [<監視対象のステート>])
code:js
useEffect(() => {
//マウント時、および「dataに変更があれば」再レンダーごとに実行されます
//変更がなければ実行されません
somefunc();
その4:最初のマウント時ににのみ実行し、再レンダー時には実行しない:useEffect(<関数>, [])
code:js
useEffect(() => {
//最初のマウント時のみ実行されます
oneTimeOnly();
}, []); //監視対象を空にする
注意:React18以降、ある条件下で上記のロジックでも2回呼び出されるようになった
条件は、
1. StrictMode(厳密モード)が有効になっているとき
2. 開発ビルドの時
対処方法としては、厳密モードをやめる(<React.StrictMode>か<StrictMode>でAppタグを囲むのをやめる)か、2回通っても良いように管理フラグを立てるか(下記)のどちらか。
code:js
const didEffect = useRef(false);
useEffect(() => {
if (!didEffect.current) {
oneTimeOnly();
didEffect.current = true;
}
}, []);
どちらにしても、リリースビルドでは2回実行されることはない。