はじめに
最近 Vue から React に乗り換えました。Vue には無かった機能や仕組みにつまづいた箇所も多かったのでまとめました。「Vue やってるけど React もやってみたいな」「最近 Vue から React に乗り換えたよ」って人は見るとちょっとだけ幸せになるかもしれません。
useState の値が変更されない
-
const [値, 値を更新する関数] = useState(値の初期値)
の形で書ける - しかし以下の形で書くと値がちゃんと更新されない
const [count, setCount] = useState(0);
const handleCountUp = () => {
// NG 😩
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
};
return (
<div className="App">
<h1>{count}</h1>
<AppButton onClick={() => handleCountUp()} />
</div>
);
- 3ずつカウントアップするのが理想だが1ずつしかカウントアップしない
解決策
-
prevState
を引数に取ることで解決 - 以前の状態 にアクセス・参照して 値を変更する
- サンプルコードだと
prevState
が引数としてよく使われてるっぽいですが、引数名はなんでも動きます
OK
const handleCountUp = () => {
setCount((prevState) => prevState + 1);
setCount((prevState) => prevState + 1);
// 引数名を prevCount としても大丈夫です
setCount((prevCount) => prevCount + 1);
};
参考
emit ではなく props で関数を渡す
- Vue では emit を用いて子コンポーネントから親コンポーネントのメソッドを発火させるのが主流でした
- React では「親コンポーネントから子コンポーネントに関数を渡す」ことで発火させるのが主流らしい
親コンポーネント
const handleCountUp = useCallback(() => {
setCountA((prevCount) => prevCount + 1);
}, []);
return (
<div className="App">
<div>
// 親で定義した関数を子コンポーネントへ渡す!!
<AppButton text="AをUP" onClick={handleCountUp} />
</div>
</div>
);
子コンポーネント
interface Props {
text: string;
onClick: () => void; // 関数の型を定義する
}
const AppButton = (props: Props) => {
const { text, onClick } = props;
return (
// 親から送られてきた関数を発火する
<Button className="app-button" onClick={() => onClick()}>
{text}
</Button>
);
};
props へ関数を渡すときにやりがちなミスと解決策
NGパターン
- 括弧をつけて渡してしまうケース
- 括弧をつけると関数が実行されてしまう
- (エディター側で怒ってくれるのでそんなにミスすることもないが、、)
// NG🙅♂️
<div className="App">
<button onClick={clickButton()}>これはボタン</button>
</div>
OKパターン ①
- アロー関数として渡す
- コールバックなので引数をとって渡すことも可能
<div className="App">
<button onClick={() => clickButton()}>これはボタン</button>
</div>
OKパターン ②
<div className="App">
<button onClick={clickButton}>これはボタン</button>
</div>
参考
17:30 くらいからの説明がわかりやすいです
パフォーマンスチューニング
(個人的に一番とっつきにくい箇所でした汗)
- React はレンダリングコストが重く、不要なレンダリングを避けないといけないらしい
- それを実現するために React.memo と useCallback がある
- ここでは「React のパフォーマンスチューニング」や「細かいレンダリングの仕組み」については言及しません
- 「Vue に比べてレンダリングが重いんだな」「考慮しないといけないんだな」程度の把握で大丈夫です
React.memo
- コンポーネント自体の不要なレンダリングを避けるために使われる
const AppButton = (props: Props) => {
const { text, onClick } = props;
return (
<Button className="app-button" onClick={() => onClick()}>
{text}
</Button>
);
};
// コンポーネント自体を memo() で囲む
export default memo(AppButton);
useCallback
- 関数自体をメモ化する
- 親から子へメソッドを渡す時は useCallback で囲んでから渡す
- そうすることでメモ化したコールバック関数を渡し、コンポーネントは再レンダリングをスキップする
const handleCountUp = useCallback(() => {
setCountA((prevCount) => prevCount + 1);
}, []);
return (
<div className="App">
<div>
<h1>{countA}</h1>
<AppButton text="AをUP" onClick={handleCountUp} />
</div>
</div>
);
参考
最後に
レンダリングの仕組みに関してはまだまだ理解が甘いので、自分でまとめて記事書こうかなと思ってます。。
こんな記事も書いたのでよかったら読んでください。