React
useState
のクロージャー地獄
useState
は再描画ごとに呼び出される関数内で保持されてるstate
と代入関数を返す関数ですが、返しているこの値は勝手に変わりません。
再描画によってコンポーネント関数が呼び出され、その中でuseState
が呼び出されて初めてその関数のクロージャー内でのみ更新されます。
クロージャー内で値が更新されても、その外の値は更新されません。
例えばこれです。
import { useState } from "react";
export const App = () => {
const [count, setCount] = useState(0);
function add(){
setCount(count + 1);
setTimeout(() => console.log(count), 1000);
}
return <div className="app">
<button onClick={add}>{count}</button>
</div>
}
setTimeoutを介すとクロージャーの外になるので、更新されず前の値がconsole
に出てきます。
対処法はあります。
代入関数の活用
TypeScriptだと変数の初期値がデフォルトになっていて面倒くさいです。
それに加え、取得するたびに再描画がされてしまいます。
let newCount = 0;
setCount(count => newCount = count);
console.log(newCount);
カスタムフック
このようにすると、上の色々を省略できます。
しかし上と同じく取得に再描画が必要です。
import React from "react";
export const useState = <T>(defaultState: T) => {
const [state, setState] = React.useState(defaultState);
const getState = (): T => {
let _state;
setState(newState => _state = newState);
return _state;
}
return [state, setState, getState];
}
import { useState } from "./usestate.js";
export const App = () => {
const [count, setCount, getCount] = useState(0);
function add(){
setCount(count + 1);
setTimeout(() => console.log(getCount()), 1000);
}
return <div className="app">
<button onClick={add}>{count}</button>
</div>
}
なぜかうまくいきませんでした。
useRef
で再描画できればなあ
メモリ食いそう
下手したらクロージャーが大量に出来るのでメモリ食いそうです。
Vue
テンプレートでそのままグローバル変数を使えない
Vueで以下のようにグローバル変数を参照しようとすると、エラーが発生します。
<script setup>
import { ref } from 'vue'
const msg = ref('Hello World!')
</script>
<template>
<h1>{{ btoa(new Date().toString()) }}</h1>
<input v-model="msg" />
</template>
この場合btoa
がエラーの原因です。
対処法 変数を作る
このようにすれば一発で解決です。
コンポーネントのdata
に保存されるためです。
しかしbind
が必要で面倒くさいのと、Web APIを保存するのは無駄を感じます。
<script setup>
import { ref } from 'vue'
const msg = ref('Hello World!')
const btoa = window.btoa.bind(window);
</script>
<template>
<h1>{{ btoa(new Date().toString()) }}</h1>
<input v-model="msg" />
</template>
結論
ReactのuseState
が返す値は勝手に変わらないので他クロージャーに値を渡すときなどに注意
Vueはテンプレートでそのまま使えないグローバル変数がある
個人としてはReactのようにJavaScriptに近いものが好きなのでReactにVueのプロキシベース変数を足してさいきょうのフレームワークが欲しい