今までがおもしろかったので、外部ストアをたくさん連れてきて、走ってもらいました。
前回で計測実験は閉幕と言いましたが、つい、1 日ほど時間があったのでやってしまいました。
wings.hatenablog.com
wings.hatenablog.com
wings.hatenablog.com
wings.hatenablog.com
参加ストアは以下の 12 選手です。
- Solid Signal
- Solid Nano Stores
- Svelte Runes
- Svelte Nano Stores
- Vue Ref
- Vue Nano Stores
- React uSES
- React Jotai
- React Legend State
- React Valtio
- React Zustand
- React Nano Stores
Redux は包まないといけないので、包まずにいける上記と違い欠場、Recoil は React 19 以降で動かないので欠場、としました。
Qwik は、Context 必須なのでこちらも欠場です。
今回の計測は「ストアの極限バケツリレー」です。
1 コンポーネントの中で最大 50 回の連鎖を行い、それを 10 コンポーネント用意し、状態が数値と文字列双方を通すので、50 * 10 * 2 = 1000 回のまでやりました。
なお、意図的に 1 コンポーネント内の連鎖数は 2・10・50 と分けて計測します(50 なのは React の連鎖制限が 50 なので)。
各コンポーネントは Astro の client:only によって分離しています。
こんなコードは普段書かないので、あくまでも実験用です。
Solid / Svelte / Vue のグローバルストアについての詳細は、こちらを参照してください。
wings.hatenablog.com
この機構は、Nano Stores や React に対しても適用しました。
では、早速いきましょう。今回も import 文と Number03〜09・String03〜09 のコンポーネントは割愛します。また、他の割愛部分は、都度記載します。
共通
ここにある dragRaceMax の数値を先程の 2・10・50 に変えた上で計測します。
export const records = {
SolidSignal: { start: 0, finished: 0 },
SolidSignalNumber: { start: 0, finished: 0 },
SolidSignalString: { start: 0, finished: 0 },
SolidNano: { start: 0, finished: 0 },
SolidNanoNumber: { start: 0, finished: 0 },
SolidNanoString: { start: 0, finished: 0 },
SvelteRunes: { start: 0, finished: 0 },
SvelteRunesNumber: { start: 0, finished: 0 },
SvelteRunesString: { start: 0, finished: 0 },
SvelteNano: { start: 0, finished: 0 },
SvelteNanoNumber: { start: 0, finished: 0 },
SvelteNanoString: { start: 0, finished: 0 },
VueRef: { start: 0, finished: 0 },
VueRefNumber: { start: 0, finished: 0 },
VueRefString: { start: 0, finished: 0 },
VueNano: { start: 0, finished: 0 },
VueNanoNumber: { start: 0, finished: 0 },
VueNanoString: { start: 0, finished: 0 },
ReactUses: { start: 0, finished: 0 },
ReactUsesNumber: { start: 0, finished: 0 },
ReactUsesString: { start: 0, finished: 0 },
ReactJotai: { start: 0, finished: 0 },
ReactJotaiNumber: { start: 0, finished: 0 },
ReactJotaiString: { start: 0, finished: 0 },
ReactLegend: { start: 0, finished: 0 },
ReactLegendNumber: { start: 0, finished: 0 },
ReactLegendString: { start: 0, finished: 0 },
ReactValtio: { start: 0, finished: 0 },
ReactValtioNumber: { start: 0, finished: 0 },
ReactValtioString: { start: 0, finished: 0 },
ReactZustand: { start: 0, finished: 0 },
ReactZustandNumber: { start: 0, finished: 0 },
ReactZustandString: { start: 0, finished: 0 },
ReactNano: { start: 0, finished: 0 },
ReactNanoNumber: { start: 0, finished: 0 },
ReactNanoString: { start: 0, finished: 0 },
};
export type Records = keyof typeof records;
const now = () => performance.now();
export const recordStart = (target: Records) => {
records[target].start = now();
};
export const recordFinished = (target: Records) => {
if (records[target].start === 0) return;
records[target].finished = now();
};
export const logTotal = (target: Records) => {
const targetNumber = `${target}Number` as Records;
const targetString = `${target}String` as Records;
if (records[targetNumber].start === 0) return;
const diffNumber = records[targetNumber].finished - records[targetNumber].start;
const diffString = records[targetString].finished - records[targetString].start;
console.log(`${targetNumber}: ${diffNumber} ミリ秒`);
console.log(`${targetString}: ${diffString} ミリ秒`);
console.log(`${target}: ${diffNumber + diffString} ミリ秒`);};
export const dragRaceMax = 50;
index.astro
<Layout title="Drag Race">
<SolidSignal />
<SolidNano />
<SvelteRunes />
<SvelteNano />
<VueRef />
<VueNano />
<ReactUses />
<ReactJotai />
<ReactLegend />
<ReactValtio />
<ReactZustand />
<ReactNano />
</Layout>
Solid Signal
SolidSignal.astro
<SolidSignalStart client:only="solid" />
<SolidSignalNumber01 client:only="solid" />
<SolidSignalString01 client:only="solid" />
ストア定義
type StartStore = 'number' | 'string' | null;
const initialStart = null as StartStore;
const solidStartStore = {
server: {
startStore: (): StartStore => initialStart,
setStart: (_: StartStore) => {},
},
client: () => {
const [ startStore, setStart ] = createSignal<StartStore>(initialStart);
return { startStore, setStart };
},
};
type NumberStore = number;
const initialNumber = 0 as NumberStore;
const solidNumberStore01 = {
server: { numberStore01: (): NumberStore => initialNumber, setNumber01: (_: NumberStore) => {} },
client: () => {
const [ numberStore01, setNumber01 ] = createSignal<NumberStore>(initialNumber);
return { numberStore01, setNumber01 };
},
};
type StringStore = string;
const initialString = '' as StringStore;
const solidStringStore01 = {
server: { stringStore01: (): StringStore => initialString, setString01: (_: StringStore) => {} },
client: () => {
const [ stringStore01, setString01 ] = createSignal<StringStore>(initialString);
return { stringStore01, setString01 };
},
};
export const _solidDragRace = {
solidStart: solidStartStore,
solidNumber01: solidNumberStore01,
solidString01: solidStringStore01,
};
Start.tsx
export default function Start() {
const { setStart } = useStore('solidStart');
const raceStart = () => {
setStart('number');
};
return (
fieldset
button onClick={ raceStart }Start Solid Signal!!!button
fieldset
)
}
Number01.tsx
export default function SignalNumber01() {
const { startStore } = useStore('solidStart');
const { numberStore01, setNumber01 } = useStore('solidNumber01');
const { setNumber02 } = useStore('solidNumber02');
createEffect(() => {
if (startStore() !== 'number') return;
recordStart('SolidSignalNumber');
setNumber01(1);
});
createEffect(() => {
if (!numberStore01()) return;
setNumber01(numberStore01() + 1);
if (numberStore01() === dragRaceMax) {
setNumber01(0);
setNumber02(1);
}
});
return null;
}
Number02.tsx
export default function SignalNumber02() {
const { numberStore02, setNumber02 } = useStore('solidNumber02');
const { setNumber03 } = useStore('solidNumber03');
createEffect(() => {
if (!numberStore02()) return;
setNumber02(numberStore02() + 1);
if (numberStore02() === dragRaceMax) {
setNumber02(0);
setNumber03(1);
}
});
return null;
}
Number10.tsx
export default function SignalNumber10() {
const { numberStore10, setNumber10 } = useStore('solidNumber10');
const { setStart } = useStore('solidStart');
createEffect(() => {
if (!numberStore10()) return;
if (numberStore10() === dragRaceMax) {
recordFinished('SolidSignalNumber');
setNumber10(0);
setStart('string');
} else {
setNumber10(numberStore10() + 1);
}
});
return null;
}
String01.tsx
export default function SignalString01() {
const { startStore } = useStore('solidStart');
const { stringStore01, setString01 } = useStore('solidString01');
const { setString02 } = useStore('solidString02');
createEffect(() => {
if (startStore() !== 'string') return;
recordStart('SolidSignalString');
setString01('1');
});
createEffect(() => {
const value = +stringStore01();
if (!value) return;
setString01((value + 1).toString());
if (value === dragRaceMax) {
setString01('');
setString02('1');
}
});
return null;
}
String02.tsx
export default function SignalString02() {
const { stringStore02, setString02 } = useStore('solidString02');
const { setString03 } = useStore('solidString03');
createEffect(() => {
const value = +stringStore02();
if (!value) return;
setString02((value + 1).toString());
if (value === dragRaceMax) {
setString02('');
setString03('1');
}
});
return null;
}
String10.tsx
export default function SignalString10() {
const { stringStore10, setString10 } = useStore('solidString10');
const { setStart } = useStore('solidStart');
createEffect(() => {
const value = +stringStore10();
if (!value) return;
if (value === dragRaceMax) {
recordFinished('SolidSignalString');
logTotal('SolidSignal');
setStart(null);
setString10('');
} else {
setString10((value + 1).toString());
}
});
return null;
}
Solid Nano Stores
SolidNano.astro
<SolidNanoStart client:only="solid" />
<SolidNanoNumber01 client:only="solid" />
<SolidNanoString01 client:only="solid" />
ストア定義
なお、Nano Stores の定義は Svelte / Vue / React でも全く同じため、これらでは省きます。
const serverStore = <T>(initialValue: T) => ({
get value() { return initialValue },
set: (newValue: T) => {},
eq: (oldValue: T, newValue: T) => false,
get: () => initialValue,
get init() { return initialValue },
get lc() { return initialValue },
listen: (listener: (value: T, oldValue: T) => void) => () => {},
notify: (oldValue?: T) => {},
off: () => {},
subscribe: (listener: (value: T, oldValue?: T) => void) => () => {},
});
type StartStore = 'number' | 'string' | null;
const initialStart = null as StartStore;
const nanoStartStore = {
server: { startStore: serverStore(initialStart) },
client: () => ({ startStore: atom(initialStart) }),
};
type NumberStore = number;
const initialNumber = 0 as NumberStore;
const nanoNumberStore01 = {
server: { numberStore01: serverStore(initialNumber) },
client: () => ({ numberStore01: atom(initialNumber) }),
};
type StringStore = string;
const initialString = '' as StringStore;
const nanoStringStore01 = {
server: { stringStore01: serverStore(initialString) },
client: () => ({ stringStore01: atom(initialString) }),
};
const nanoStores = {
nanoStart: nanoStartStore,
nanoNumber01: nanoNumberStore01,
nanoString01: nanoStringStore01,
} as const;
export type NanoStores = typeof nanoStores;
export type NanoStoreKeys = keyof NanoStores;
const createServerStore = (key: NanoStoreKeys): NanoStores[typeof key]['server'] => nanoStores[key].server;
type CreateClientStore = ReturnType<NanoStores[NanoStoreKeys]['client']>;
const createClientStore = (key: NanoStoreKeys): CreateClientStore => nanoStores[key].client();
type ClientStoreInstance = Record<NanoStoreKeys, CreateClientStore | null>;
const clientStoreInstance = Object.fromEntries(Object.keys(nanoStores).map(key => ([ key, null ]))) as ClientStoreInstance;
export const useNanoStore = <K extends NanoStoreKeys, R extends ReturnType<NanoStores[K]['client']>>(key: K) => {
if (typeof window === 'undefined') return createServerStore(key) as R;
if (!clientStoreInstance[key]) {
clientStoreInstance[key] = createClientStore(key);
}
return clientStoreInstance[key] as R;
};
Start.tsx
export default function Start() {
const { startStore } = useNanoStore('nanoStart');
const raceStart = () => {
startStore.set('number');
};
return (
fieldset
button onClick={ raceStart }Start Solid Nano Stores!!!button
fieldset
)
}
Number01.tsx
export default function NanoNumber01() {
const { startStore } = useNanoStore('nanoStart');
const start = useStore(startStore);
const { numberStore01 } = useNanoStore('nanoNumber01');
const store = useStore(numberStore01);
const { numberStore02 } = useNanoStore('nanoNumber02');
createEffect(() => {
if (start() !== 'number') return;
recordStart('SolidNanoNumber');
numberStore01.set(1);
});
createEffect(() => {
if (!store()) return;
numberStore01.set(store() + 1);
if (store() === dragRaceMax) {
numberStore01.set(0);
numberStore02.set(1);
}
});
return null;
}
Number02.tsx
export default function NanoNumber02() {
const { numberStore02 } = useNanoStore('nanoNumber02');
const store = useStore(numberStore02);
const { numberStore03 } = useNanoStore('nanoNumber03');
createEffect(() => {
if (!store()) return;
numberStore02.set(store() + 1);
if (store() === dragRaceMax) {
numberStore02.set(0);
numberStore03.set(1);
}
});
return null;
}
Number10.tsx
export default function NanoNumber10() {
const { numberStore10 } = useNanoStore('nanoNumber10');
const store = useStore(numberStore10);
const { startStore } = useNanoStore('nanoStart');
createEffect(() => {
if (!store()) return;
if (store() === dragRaceMax) {
recordFinished('SolidNanoNumber');
numberStore10.set(0);
startStore.set('string');
} else {
numberStore10.set(store() + 1);
}
});
return null;
}
String01.tsx
export default function NanoString01() {
const { startStore } = useNanoStore('nanoStart');
const start = useStore(startStore);
const { stringStore01 } = useNanoStore('nanoString01');
const store = useStore(stringStore01);
const { stringStore02 } = useNanoStore('nanoString02');
createEffect(() => {
if (start() !== 'string') return;
recordStart('SolidNanoString');
stringStore01.set('1');
});
createEffect(() => {
const value = +store();
if (!value) return;
stringStore01.set((value + 1).toString());
if (value === dragRaceMax) {
stringStore01.set('');
stringStore02.set('1');
}
});
return null;
}
String02.tsx
export default function NanoString02() {
const { stringStore02 } = useNanoStore('nanoString02');
const store = useStore(stringStore02);
const { stringStore03 } = useNanoStore('nanoString03');
createEffect(() => {
const value = +store();
if (!value) return;
stringStore02.set((value + 1).toString());
if (value === dragRaceMax) {
stringStore02.set('');
stringStore03.set('1');
}
});
return null;
}
String10.tsx
export default function NanoString10() {
const { stringStore10 } = useNanoStore('nanoString10');
const store = useStore(stringStore10);
const { startStore } = useNanoStore('nanoStart');
createEffect(() => {
const value = +store();
if (!value) return;
if (value === dragRaceMax) {
recordFinished('SolidNanoString');
logTotal('SolidNano');
startStore.set(null);
stringStore10.set('');
} else {
stringStore10.set((value + 1).toString());
}
});
return null;
}
Svelte Runes
SvelteRunes.astro
<SvelteRunesStart client:only="svelte" />
<SvelteRunesNumber01 client:only="svelte" />
<SvelteRunesString01 client:only="svelte" />
ストア定義
type StartStore = 'number' | 'string' | null;
const initialStart = null as StartStore;
const svelteStartStore = {
server: {
startStore: (): StartStore => initialStart,
setStart: (_: StartStore) => {},
},
client: () => {
let startStore = $state<StartStore>(initialStart);
const setStart = (value: StartStore) => { startStore = value; };
return {
startStore: () => startStore, setStart,
};
},
};
type NumberStore = number;
const initialNumber = 0 as NumberStore;
const svelteNumberStore01 = {
server: { numberStore01: (): NumberStore => initialNumber, setNumber01: (_: NumberStore) => {} },
client: () => {
let numberStore01 = $state<NumberStore>(initialNumber);
return { numberStore01: () => numberStore01, setNumber01: (value: NumberStore) => { numberStore01 = value; } };
},
};
type StringStore = string;
const initialString = '' as StringStore;
const svelteStringStore01 = {
server: { stringStore01: (): StringStore => initialString, setString01: (_: StringStore) => {} },
client: () => {
let stringStore01 = $state<StringStore>(initialString);
return { stringStore01: () => stringStore01, setString01: (value: StringStore) => { stringStore01 = value; } };
},
};
export const _svelteDragRace = {
svelteStart: svelteStartStore,
svelteNumber01: svelteNumberStore01,
svelteString01: svelteStringStore01,
};
Start.svelte
<script lang="ts">
const { setStart } = useStore('svelteStart');
const raceStart = () => {
setStart('number');
};
</script>
<fieldset>
<button onclick={ raceStart }>Start Svelte Runes!!!</button>
</fieldset>
Number01.svelte
<script lang="ts">
const { startStore } = useStore('svelteStart');
const { numberStore01, setNumber01 } = useStore('svelteNumber01');
const { setNumber02 } = useStore('svelteNumber02');
$effect(() => {
if (startStore() !== 'number') return;
recordStart('SvelteRunesNumber');
setNumber01(1);
});
$effect(() => {
if (!numberStore01()) return;
setNumber01(numberStore01() + 1);
if (numberStore01() === dragRaceMax) {
setNumber01(0);
setNumber02(1);
}
});
</script>
Number02.svelte
<script lang="ts">
const { numberStore02, setNumber02 } = useStore('svelteNumber02');
const { setNumber03 } = useStore('svelteNumber03');
$effect(() => {
if (!numberStore02()) return;
setNumber02(numberStore02() + 1);
if (numberStore02() === dragRaceMax) {
setNumber02(0);
setNumber03(1);
}
});
</script>
Number10.svelte
<script lang="ts">
const { numberStore10, setNumber10 } = useStore('svelteNumber10');
const { setStart } = useStore('svelteStart');
$effect(() => {
if (!numberStore10()) return;
if (numberStore10() === dragRaceMax) {
recordFinished('SvelteRunesNumber');
setNumber10(0);
setStart('string');
} else {
setNumber10(numberStore10() + 1);
}
});
</script>
String01.svelte
<script lang="ts">
const { startStore } = useStore('svelteStart');
const { stringStore01, setString01 } = useStore('svelteString01');
const { setString02 } = useStore('svelteString02');
$effect(() => {
if (startStore() !== 'string') return;
recordStart('SvelteRunesString');
setString01('1');
});
$effect(() => {
const value = +stringStore01();
if (!value) return;
setString01((value + 1).toString());
if (value === dragRaceMax) {
setString01('');
setString02('1');
}
});
</script>
String02.svelte
<script lang="ts">
const { stringStore02, setString02 } = useStore('svelteString02');
const { setString03 } = useStore('svelteString03');
$effect(() => {
const value = +stringStore02();
if (!value) return;
setString02((value + 1).toString());
if (value === dragRaceMax) {
setString02('');
setString03('1');
}
});
</script>
String10.svelte
<script lang="ts">
const { stringStore10, setString10 } = useStore('svelteString10');
const { setStart } = useStore('svelteStart');
$effect(() => {
const value = +stringStore10();
if (!value) return;
if (value === dragRaceMax) {
recordFinished('SvelteRunesString');
logTotal('SvelteRunes');
setStart(null);
setString10('');
} else {
setString10((value + 1).toString());
}
});
</script>
Svelte Nano Stores
SvelteNano.astro
<SvelteNanoStart client:only="svelte" />
<SvelteNanoNumber01 client:only="svelte" />
<SvelteNanoString01 client:only="svelte" />
ストア定義
Solid Nano Stores と同一です。
Start.svelte
<script lang="ts">
const { startStore } = useNanoStore('nanoStart');
const raceStart = () => {
startStore.set('number');
};
</script>
<fieldset>
<button onclick={ raceStart }>Start Svelte Nano Stores!!!</button>
</fieldset>
Number01.svelte
<script lang="ts">
const { startStore } = useNanoStore('nanoStart');
const start = useStore(startStore);
const { numberStore01 } = useNanoStore('nanoNumber01');
const store = useStore(numberStore01);
const { numberStore02 } = useNanoStore('nanoNumber02');
$effect(() => {
if (start.current !== 'number') return;
recordStart('SvelteNanoNumber');
numberStore01.set(1);
});
$effect(() => {
if (!store.current) return;
numberStore01.set(store.current + 1);
if (store.current === dragRaceMax) {
numberStore01.set(0);
numberStore02.set(1);
}
});
</script>
Number02.svelte
<script lang="ts">
const { numberStore02 } = useNanoStore('nanoNumber02');
const store = useStore(numberStore02);
const { numberStore03 } = useNanoStore('nanoNumber03');
$effect(() => {
if (!store.current) return;
numberStore02.set(store.current + 1);
if (store.current === dragRaceMax) {
numberStore02.set(0);
numberStore03.set(1);
}
});
</script>
Number10.svelte
<script lang="ts">
const { numberStore10 } = useNanoStore('nanoNumber10');
const store = useStore(numberStore10);
const { startStore } = useNanoStore('nanoStart');
$effect(() => {
if (!store.current) return;
if (store.current === dragRaceMax) {
recordFinished('SvelteNanoNumber');
numberStore10.set(0);
startStore.set('string');
} else {
numberStore10.set(store.current + 1);
}
});
</script>
String01.svelte
<script lang="ts">
const { startStore } = useNanoStore('nanoStart');
const start = useStore(startStore);
const { stringStore01 } = useNanoStore('nanoString01');
const store = useStore(stringStore01);
const { stringStore02 } = useNanoStore('nanoString02');
$effect(() => {
if (start.current !== 'string') return;
recordStart('SvelteNanoString');
stringStore01.set('1');
});
$effect(() => {
const value = +store.current;
if (!value) return;
stringStore01.set((value + 1).toString());
if (value === dragRaceMax) {
stringStore01.set('');
stringStore02.set('1');
}
});
</script>
String02.svelte
<script lang="ts">
const { stringStore02 } = useNanoStore('nanoString02');
const store = useStore(stringStore02);
const { stringStore03 } = useNanoStore('nanoString03');
$effect(() => {
const value = +store.current;
if (!value) return;
stringStore02.set((value + 1).toString());
if (value === dragRaceMax) {
stringStore02.set('');
stringStore03.set('1');
}
});
</script>
String10.svelte
<script lang="ts">
const { stringStore10 } = useNanoStore('nanoString10');
const store = useStore(stringStore10);
const { startStore } = useNanoStore('nanoStart');
$effect(() => {
const value = +store.current;
if (!value) return;
if (value === dragRaceMax) {
recordFinished('SvelteNanoString');
logTotal('SvelteNano');
startStore.set(null);
stringStore10.set('');
} else {
stringStore10.set((value + 1).toString());
}
});
</script>
Vue Ref
VueRef.astro
<VueRefStart client:only="vue" />
<VueRefNumber01 client:only="vue" />
<VueRefString01 client:only="vue" />
ストア定義
type StartStore = 'number' | 'string' | null;
const initialStart = null as StartStore;
const refStartStore = {
server: {
startStore: (): StartStore => initialStart,
setStart: (_: StartStore) => {},
},
client: () => {
const startStore = ref<StartStore>(initialStart);
const setStart = (value: StartStore) => { startStore.value = value; };
return {
startStore: () => startStore.value, setStart,
};
},
};
type NumberStore = number;
const initialNumber = 0 as NumberStore;
const refNumberStore01 = {
server: { numberStore01: (): NumberStore => initialNumber, setNumber01: (_: NumberStore) => {} },
client: () => {
const numberStore01 = ref<NumberStore>(initialNumber);
return { numberStore01: () => numberStore01.value, setNumber01: (value: NumberStore) => { numberStore01.value = value; } };
},
};
type StringStore = string;
const initialString = '' as StringStore;
const refStringStore01 = {
server: { stringStore01: (): StringStore => initialString, setNumber01: (_: StringStore) => {} },
client: () => {
const stringStore01 = ref<StringStore>(initialString);
return { stringStore01: () => stringStore01.value, setNumber01: (value: StringStore) => { stringStore01.value = value; } };
},
};
export const _vueDragRace = {
vueStart: refStartStore,
vueNumber01: refNumberStore01,
vueString01: refStringStore01,
};
Start.vue
<script setup lang="ts">
const { setStart } = useStore('vueStart');
const raceStart = () => {
setStart('number');
};
</script>
<template>
<fieldset>
<button @click="raceStart">Start Vue Ref!!!</button>
</fieldset>
</template>
Number01.vue
<script setup lang="ts">
const { startStore } = useStore('vueStart');
const { numberStore01, setNumber01 } = useStore('vueNumber01');
const { setNumber02 } = useStore('vueNumber02');
watch(
() => startStore(),
() => {
if (startStore() !== 'number') return;
recordStart('VueRefNumber');
setNumber01(1);
}, { flush: 'pre' }
);
watch(
() => numberStore01(),
() => {
if (!numberStore01()) return;
setNumber01(numberStore01() + 1);
if (numberStore01() === dragRaceMax) {
setNumber01(0);
setNumber02(1);
}
}, { flush: 'pre' }
);
</script>
<template>
</template>
Number02.vue
<script setup lang="ts">
const { numberStore02, setNumber02 } = useStore('vueNumber02');
const { setNumber03 } = useStore('vueNumber03');
watch(
() => numberStore02(),
() => {
if (!numberStore02()) return;
setNumber02(numberStore02() + 1);
if (numberStore02() === dragRaceMax) {
setNumber02(0);
setNumber03(1);
}
},
{ flush: 'pre' }
);
</script>
<template>
</template>
Number10.vue
<script setup lang="ts">
const { numberStore10, setNumber10 } = useStore('vueNumber10');
const { setStart } = useStore('vueStart');
watch(
() => numberStore10(),
() => {
if (!numberStore10()) return;
if (numberStore10() === dragRaceMax) {
recordFinished('VueRefNumber');
setNumber10(0);
setStart('string');
} else {
setNumber10(numberStore10() + 1);
}
},
{ flush: 'pre' }
);
</script>
<template>
</template>
String01.vue
<script setup lang="ts">
const { startStore } = useStore('vueStart');
const { stringStore01, setNumber01 } = useStore('vueString01');
const { setNumber02 } = useStore('vueString02');
watch(
() => startStore(),
() => {
if (startStore() !== 'string') return;
recordStart('VueRefString');
setNumber01('1');
}, { flush: 'pre' }
);
watch(
() => stringStore01(),
() => {
const value = +stringStore01();
if (!value) return;
setNumber01((value + 1).toString());
if (value === dragRaceMax) {
setNumber01('');
setNumber02('1');
}
}, { flush: 'pre' }
);
</script>
<template>
</template>
String02.vue
<script setup lang="ts">
const { stringStore02, setNumber02 } = useStore('vueString02');
const { setNumber03 } = useStore('vueString03');
watch(
() => stringStore02(),
() => {
const value = +stringStore02();
if (!value) return;
setNumber02((value + 1).toString());
if (value === dragRaceMax) {
setNumber02('');
setNumber03('1');
}
},
{ flush: 'pre' }
);
</script>
<template>
</template>
String10.vue
<script setup lang="ts">
const { stringStore10, setNumber10 } = useStore('vueString10');
const { setStart } = useStore('vueStart');
watch(
() => stringStore10(),
() => {
const value = +stringStore10();
if (!value) return;
if (value === dragRaceMax) {
recordFinished('VueRefString');
logTotal('VueRef');
setStart(null);
setNumber10('');
} else {
setNumber10((value + 1).toString());
}
},
{ flush: 'pre' }
);
</script>
<template>
</template>
Vue Nano Stores
VueNano.astro
ストア定義
Solid Nano Stores と同一です。
Start.vue
<script setup lang="ts">
const { $startStore } = useNanoStore('nanoStart');
const raceStart = () => {
$startStore.set('number');
};
</script>
<template>
<fieldset>
<button @click="raceStart">Start Vue Nano Stores!!!</button>
</fieldset>
</template>
Number01.vue
<script setup lang="ts">
const { $startStore } = useNanoStore('nanoStart');
const start = useStore($startStore);
const { $numberStore01 } = useNanoStore('nanoNumber01');
const store = useStore($numberStore01);
const { $numberStore02 } = useNanoStore('nanoNumber02');
watch(
() => start.value,
() => {
if (start.value !== 'number') return;
recordStart('VueNanoNumber');
$numberStore01.set(1);
}, { flush: 'pre' }
);
watch(
() => store.value,
() => {
if (!store.value) return;
$numberStore01.set(store.value + 1);
if (store.value === dragRaceMax) {
$numberStore01.set(0);
$numberStore02.set(1);
}
}, { flush: 'pre' }
);
</script>
<template>
</template>
Number02.vue
<script setup lang="ts">
const { $numberStore02 } = useNanoStore('nanoNumber02');
const store = useStore($numberStore02);
const { $numberStore03 } = useNanoStore('nanoNumber03');
watch(
() => store.value,
() => {
if (!store.value) return;
$numberStore02.set(store.value + 1);
if (store.value === dragRaceMax) {
$numberStore02.set(0);
$numberStore03.set(1);
}
},
{ flush: 'pre' }
);
</script>
<template>
</template>
Number10.vue
<script setup lang="ts">
const { $numberStore10 } = useNanoStore('nanoNumber10');
const store = useStore($numberStore10);
const { $startStore } = useNanoStore('nanoStart');
watch(
() => store.value,
() => {
if (!store.value) return;
if (store.value === dragRaceMax) {
recordFinished('VueNanoNumber');
$numberStore10.set(0);
$startStore.set('string');
} else {
$numberStore10.set(store.value + 1);
}
},
{ flush: 'pre' }
);
</script>
<template>
</template>
String01.vue
<script setup lang="ts">
const { $startStore } = useNanoStore('nanoStart');
const start = useStore($startStore);
const { $stringStore01 } = useNanoStore('nanoString01');
const store = useStore($stringStore01);
const { $stringStore02 } = useNanoStore('nanoString02');
watch(
() => start.value,
() => {
if (start.value !== 'string') return;
recordStart('VueNanoString');
$stringStore01.set('1');
}, { flush: 'pre' }
);
watch(
() => store.value,
() => {
const value = +store.value;
if (!value) return;
$stringStore01.set((value + 1).toString());
if (value === dragRaceMax) {
$stringStore01.set('');
$stringStore02.set('1');
}
}, { flush: 'pre' }
);
</script>
<template>
</template>
String02.vue
<script setup lang="ts">
const { $stringStore02 } = useNanoStore('nanoString02');
const store = useStore($stringStore02);
const { $stringStore03 } = useNanoStore('nanoString03');
watch(
() => store.value,
() => {
const value = +store.value;
if (!value) return;
$stringStore02.set((value + 1).toString());
if (value === dragRaceMax) {
$stringStore02.set('');
$stringStore03.set('1');
}
},
{ flush: 'pre' }
);
</script>
<template>
</template>
String10.vue
<script setup lang="ts">
const { $stringStore10 } = useNanoStore('nanoString10');
const store = useStore($stringStore10);
const { $startStore } = useNanoStore('nanoStart');
watch(
() => store.value,
() => {
const value = +store.value;
if (!value) return;
if (value === dragRaceMax) {
recordFinished('VueNanoString');
logTotal('VueNano');
$startStore.set(null);
$stringStore10.set('');
} else {
$stringStore10.set((value + 1).toString());
}
},
{ flush: 'pre' }
);
</script>
<template>
</template>
React 用のストア供給ファイル
上記の Solid / Svelte / Vue 用の機構を React へ移植したものです。Zustand のみはやや細工が必要なので、ストア定義ファイルに個別に記載しています。
const allStores = {
..._uSESStores,
..._jotaiStores,
..._legendStores,
..._valtioStores,
..._nanoStores,
} as const;
export type AllStores = typeof allStores;
export type AllStoreKeys = keyof AllStores;
const createServerStore = (key: AllStoreKeys): AllStores[typeof key]['server'] => allStores[key].server;
type CreateClientStore = ReturnType<AllStores[AllStoreKeys]['client']>;
const createClientStore = (key: AllStoreKeys): CreateClientStore => allStores[key].client();
type ClientStoreInstance = Record<AllStoreKeys, CreateClientStore | null>;
const clientStoreInstance = Object.fromEntries(Object.keys(allStores).map(key => ([ key, null ]))) as ClientStoreInstance;
export const useReactStore = <K extends AllStoreKeys, R extends ReturnType<AllStores[K]['client']>>(key: K) => {
if (typeof window === 'undefined') return createServerStore(key) as R;
if (!clientStoreInstance[key]) {
clientStoreInstance[key] = createClientStore(key);
}
return clientStoreInstance[key] as R;
};
React uSES
ReactUses.astro
<ReactUsesStart client:only="react" />
<ReactUsesNumber01 client:only="react" />
<ReactUsesString01 client:only="react" />
ストア定義
type Listener = () => void;
const serverStore = <T>(value: T) => ({
getServerSnapshot() { return value; },
getSnapshot() { return value; },
subscribe(_: Listener) {},
set(_: T) {},
});
const clientStore = <T>(initialValue: T) => () => {
let store = initialValue;
let listeners: Listener[] = [];
const subscribe = (listener: Listener) => {
listeners = [...listeners, listener];
return () => {
listeners = listeners.filter(l => l !== listener);
};
};
const getServerSnapshot = () => initialValue;
const getSnapshot = () => store;
const set = (value: T) => {
store = value;
for (let listener of listeners) {
listener();
}
};
return { subscribe, getServerSnapshot, getSnapshot, set };
};
const initialStart = null as 'number' | 'string' | null;
const serverStartStore = { ...serverStore(initialStart) };
const usesStartStore = { server: serverStartStore, client: clientStore(initialStart) };
const initialNumber: number = 0;
const serverNumberStore = { ...serverStore(initialNumber) };
const usesNumberStore01 = { server: serverNumberStore, client: clientStore(initialNumber) };
const initialString: string = '';
const serverStringStore = { ...serverStore(initialString) };
const usesStringStore01 = { server: serverStringStore, client: clientStore(initialString) };
export const _uSESStores = {
usesStart: usesStartStore,
usesNumber01: usesNumberStore01,
usesString01: usesStringStore01,
};
Start.tsx
export default function UsesStart() {
const start = useReactStore('usesStart');
const raceStart = () => {
start.set('number');
};
return (
fieldset
button onClick={ raceStart }Start React uSES!!!button
fieldset
)
}
Number01.tsx
export default function UsesNumber01() {
const start = useReactStore('usesStart');
const startStore = useSyncExternalStore(start.subscribe, start.getSnapshot, start.getServerSnapshot);
const store01 = useReactStore('usesNumber01');
const storeValue = useSyncExternalStore(store01.subscribe, store01.getSnapshot, store01.getServerSnapshot);
const store02 = useReactStore('usesNumber02');
useLayoutEffect(() => {
if (startStore !== 'number') return;
recordStart('ReactUsesNumber');
store01.set(1);
}, [ startStore ]);
useLayoutEffect(() => {
if (!storeValue) return;
store01.set(storeValue + 1);
if (storeValue === dragRaceMax) {
store01.set(0);
store02.set(1);
}
}, [ storeValue ]);
return null;
}
Number02.tsx
export default function UsesNumber02() {
const store02 = useReactStore('usesNumber02');
const storeValue = useSyncExternalStore(store02.subscribe, store02.getSnapshot, store02.getServerSnapshot);
const store03 = useReactStore('usesNumber03');
useLayoutEffect(() => {
if (!storeValue) return;
store02.set(storeValue + 1);
if (storeValue === dragRaceMax) {
store02.set(0);
store03.set(1);
}
}, [ storeValue ]);
return null;
}
Number10.tsx
export default function UsesNumber10() {
const store10 = useReactStore('usesNumber10');
const storeValue = useSyncExternalStore(store10.subscribe, store10.getSnapshot, store10.getServerSnapshot);
const start = useReactStore('usesStart');
useLayoutEffect(() => {
if (!storeValue) return;
if (storeValue === dragRaceMax) {
recordFinished('ReactUsesNumber');
store10.set(0);
start.set('string');
} else {
store10.set(storeValue + 1);
}
}, [ storeValue ]);
return null;
}
String01.tsx
export default function UsesString01() {
const start = useReactStore('usesStart');
const startStore = useSyncExternalStore(start.subscribe, start.getSnapshot, start.getServerSnapshot);
const store01 = useReactStore('usesString01');
const storeValue = useSyncExternalStore(store01.subscribe, store01.getSnapshot, store01.getServerSnapshot);
const store02 = useReactStore('usesString02');
useLayoutEffect(() => {
if (startStore !== 'string') return;
recordStart('ReactUsesString');
store01.set('1');
}, [ startStore ]);
useLayoutEffect(() => {
const value = +storeValue;
if (!value) return;
store01.set((value + 1).toString());
if (value === dragRaceMax) {
store01.set('');
store02.set('1');
}
}, [ storeValue ]);
return null;
}
String02.tsx
export default function UsesString02() {
const store02 = useReactStore('usesString02');
const storeValue = useSyncExternalStore(store02.subscribe, store02.getSnapshot, store02.getServerSnapshot);
const store03 = useReactStore('usesString03');
useLayoutEffect(() => {
const value = +storeValue;
if (!value) return;
store02.set((value + 1).toString());
if (value === dragRaceMax) {
store02.set('');
store03.set('1');
}
}, [ storeValue ]);
return null;
}
String10.tsx
export default function UsesString10() {
const store10 = useReactStore('usesString10');
const storeValue = useSyncExternalStore(store10.subscribe, store10.getSnapshot, store10.getServerSnapshot);
const start = useReactStore('usesStart');
useLayoutEffect(() => {
const value = +storeValue;
if (!value) return;
if (value === dragRaceMax) {
recordFinished('ReactUsesString');
logTotal('ReactUses');
start.set(null);
store10.set('');
} else {
store10.set((value + 1).toString());
}
}, [ storeValue ]);
return null;
}
React Jotai
ReactJotai.astro
<ReactJotaiStart client:only="react" />
<ReactJotaiNumber01 client:only="react" />
<ReactJotaiString01 client:only="react" />
ストア定義
type StartStore = 'number' | 'string' | null;
const initialStart = null as StartStore;
const jotaiStartStore = {
server: ({ startStore: initialStart }),
client: () => ({ startStore: atom(initialStart) }),
};
type NumberStore = number;
const initialNumber = 0 as NumberStore;
const jotaiNumberStore01 = {
server: ({ numberStore01: initialNumber }),
client: () => ({ numberStore01: atom(initialNumber)}),
};
type StringStore = string;
const initialString = '' as StringStore;
const jotaiStringStore01 = {
server: ({ stringStore01: initialString }),
client: () => ({ stringStore01: atom(initialString)}),
};
export const _jotaiStores = {
jotaiStart: jotaiStartStore,
jotaiNumber01: jotaiNumberStore01,
jotaiString01: jotaiStringStore01,
};
Start.tsx
export default function JotaiStart() {
const { startStore } = useReactStore('jotaiStart');
const [ _, setStart ] = useAtom(startStore);
const raceStart = () => {
setStart('number');
};
return (
fieldset
button onClick={ raceStart }Start React Jotai!!!button
fieldset
)
}
Number01.tsx
export default function JotaiNumber01() {
const { startStore } = useReactStore('jotaiStart');
const [ start ] = useAtom(startStore);
const { numberStore01 } = useReactStore('jotaiNumber01');
const [ store01, setNumber01 ] = useAtom(numberStore01);
const { numberStore02 } = useReactStore('jotaiNumber02');
const [ _, setNumber02 ] = useAtom(numberStore02);
useLayoutEffect(() => {
if (start !== 'number') return;
recordStart('ReactJotaiNumber');
setNumber01(1);
}, [ start ]);
useLayoutEffect(() => {
if (!store01) return;
setNumber01(store01 + 1);
if (store01 === dragRaceMax) {
setNumber01(0);
setNumber02(1);
}
}, [ store01 ]);
return null;
}
Number02.tsx
export default function JotaiNumber02() {
const { numberStore02 } = useReactStore('jotaiNumber02');
const [ store02, setNumber02 ] = useAtom(numberStore02);
const { numberStore03 } = useReactStore('jotaiNumber03');
const [ _, setNumber03 ] = useAtom(numberStore03);
useLayoutEffect(() => {
if (!store02) return;
setNumber02(store02 + 1);
if (store02 === dragRaceMax) {
setNumber02(0);
setNumber03(1);
}
}, [ store02 ]);
return null;
}
Number10.tsx
export default function JotaiNumber10() {
const { numberStore10 } = useReactStore('jotaiNumber10');
const [ store10, setNumber10 ] = useAtom(numberStore10);
const { startStore } = useReactStore('jotaiStart');
const [ _, setStart ] = useAtom(startStore);
useLayoutEffect(() => {
if (!store10) return;
if (store10 === dragRaceMax) {
recordFinished('ReactJotaiNumber');
setNumber10(0);
setStart('string');
} else {
setNumber10(store10 + 1);
}
}, [ store10 ]);
return null;
}
String01.tsx
export default function JotaiString01() {
const { startStore } = useReactStore('jotaiStart');
const [ start ] = useAtom(startStore);
const { stringStore01 } = useReactStore('jotaiString01');
const [ store01, setString01 ] = useAtom(stringStore01);
const { stringStore02 } = useReactStore('jotaiString02');
const [ _, setString02 ] = useAtom(stringStore02);
useLayoutEffect(() => {
if (start !== 'string') return;
recordStart('ReactJotaiString');
setString01('1');
}, [ start ]);
useLayoutEffect(() => {
const value = +store01;
if (!value) return;
setString01((value + 1).toString());
if (value === dragRaceMax) {
setString01('');
setString02('1');
}
}, [ store01 ]);
return null;
}
String02.tsx
export default function JotaiString02() {
const { stringStore02 } = useReactStore('jotaiString02');
const [ store02, setString02 ] = useAtom(stringStore02);
const { stringStore03 } = useReactStore('jotaiString03');
const [ _, setString03 ] = useAtom(stringStore03);
useLayoutEffect(() => {
const value = +store02;
if (!value) return;
setString02((value + 1).toString());
if (value === dragRaceMax) {
setString02('');
setString03('1');
}
}, [ store02 ]);
return null;
}
String10.tsx
export default function JotaiString10() {
const { stringStore10 } = useReactStore('jotaiString10');
const [ store10, setString10 ] = useAtom(stringStore10);
const { startStore } = useReactStore('jotaiStart');
const [ start, setStart ] = useAtom(startStore);
useLayoutEffect(() => {
const value = +store10;
if (!value) return;
if (value === dragRaceMax) {
recordFinished('ReactJotaiString');
logTotal('ReactJotai');
setStart(null);
setString10('');
} else {
setString10((value + 1).toString());
}
}, [ store10 ]);
return null;
}
React Legend State
ReactLegend.astro
<ReactLegendStart client:only="react" />
<ReactLegendNumber01 client:only="react" />
<ReactLegendString01 client:only="react" />
ストア定義
const serverStore = <T>(initialValue: T) => ({
delete: () => {},
set: (_: T) => {},
peek: () => initialValue,
get: (_?: TrackingType | GetOptions) => initialValue,
onChange: (cb: ListenerFn<number>, options?: {}) => {},
});
type StartStore = 'number' | 'string' | null;
const initialStart = null as StartStore;
const legendStartStore$ = {
server: ({ startStore$: serverStore(initialStart) }),
client: () => ({ startStore$: observable<StartStore>(initialStart) }),
};
type NumberStore = number;
const initialNumber = 0 as NumberStore;
const legendNumberStore01$ = {
server: ({ numberStore01$: serverStore(initialNumber) }),
client: () => ({ numberStore01$: observable<NumberStore>(initialNumber) }),
};
type StringStore = string;
const initialString = '' as StringStore;
const legendStringStore01$ = {
server: ({ stringStore01$: serverStore(initialString) }),
client: () => ({ stringStore01$: observable<StringStore>(initialString) }),
};
export const _legendStores = {
legendStart: legendStartStore$,
legendNumber01: legendNumberStore01$,
legendString01: legendStringStore01$,
};
Start.tsx
export default function LegendStart() {
const { startStore$ } = useReactStore('legendStart');
const raceStart = () => {
startStore$.set('number');
};
return (
fieldset
button onClick={ raceStart }Start React Legend State!!!button
fieldset
)
}
Number01.tsx
export default function LegendNumber01() {
const { startStore$ } = useReactStore('legendStart');
const { numberStore01$ } = useReactStore('legendNumber01');
const { numberStore02$ } = useReactStore('legendNumber02');
useObserve(() => {
if (startStore$.get() !== 'number') return;
recordStart('ReactLegendNumber');
numberStore01$.set(1);
});
useObserve(() => {
const value = numberStore01$.get();
if (!value) return;
numberStore01$.set(value + 1);
if (value === dragRaceMax) {
numberStore01$.set(0);
numberStore02$.set(1);
}
});
return null;
}
Number02.tsx
export default function LegendNumber02() {
const { numberStore02$ } = useReactStore('legendNumber02');
const { numberStore03$ } = useReactStore('legendNumber03');
useObserve(() => {
const value = numberStore02$.get();
if (!value) return;
numberStore02$.set(value + 1);
if (value === dragRaceMax) {
numberStore02$.set(0);
numberStore03$.set(1);
}
});
return null;
}
Number10.tsx
export default function LegendNumber10() {
const { numberStore10$ } = useReactStore('legendNumber10');
const { startStore$ } = useReactStore('legendStart');
useObserve(() => {
const value = numberStore10$.get();
if (!value) return;
if (value === dragRaceMax) {
recordFinished('ReactLegendNumber');
numberStore10$.set(0);
startStore$.set('string');
} else {
numberStore10$.set(value + 1);
}
});
return null;
}
String01.tsx
export default function LegendString01() {
const { startStore$ } = useReactStore('legendStart');
const { stringStore01$ } = useReactStore('legendString01');
const { stringStore02$ } = useReactStore('legendString02');
useObserve(() => {
if (startStore$.get() !== 'string') return;
recordStart('ReactLegendString');
stringStore01$.set('1');
});
useObserve(() => {
const value = +stringStore01$.get();
if (!value) return;
stringStore01$.set((value + 1).toString());
if (value === dragRaceMax) {
stringStore01$.set('');
stringStore02$.set('1');
}
});
return null;
}
String02.tsx
export default function LegendString02() {
const { stringStore02$ } = useReactStore('legendString02');
const { stringStore03$ } = useReactStore('legendString03');
useObserve(() => {
const value = +stringStore02$.get();
if (!value) return;
stringStore02$.set((value + 1).toString());
if (value === dragRaceMax) {
stringStore02$.set('');
stringStore03$.set('1');
}
});
return null;
}
String10.tsx
export default function LegendString10() {
const { stringStore10$ } = useReactStore('legendString10');
const { startStore$ } = useReactStore('legendStart');
useObserve(() => {
const store = +stringStore10$.get();
if (!store) return;
if (store === dragRaceMax) {
recordFinished('ReactLegendString');
logTotal('ReactLegend');
startStore$.set(null);
stringStore10$.set('');
} else {
stringStore10$.set((store + 1).toString());
}
});
return null;
}
React Valtio
ReactValtio.astro
<ReactValtioStart client:only="react" />
<ReactValtioNumber01 client:only="react" />
<ReactValtioString01 client:only="react" />
ストア定義
const serverStore = <T>(initialValue: T) => ({
startStore: initialValue,
setStart: (_: T) => {},
});
type StartStore = { value: 'number' | 'string' | null };
const initialStart: StartStore['value'] = null;
const valtioStartStore = {
server: serverStore(initialStart),
client: () => {
const startStore = proxy<StartStore>({ value: initialStart });
return { startStore, setStart: (value: StartStore['value']) => { startStore.value = value; } };
},
};
type NumberStore = { value: number };
const initialNumber: NumberStore['value'] = 0;
const valtioNumberStore01 = {
server: serverStore(initialNumber),
client: () => {
const numberStore01 = proxy<NumberStore>({ value: initialNumber });
return { numberStore01, setNumber01: (value: NumberStore['value']) => { numberStore01.value = value; } };
},
};
type StringStore = { value: string };
const initialString: StringStore['value'] = '';
const valtioStringStore01 = {
server: serverStore(initialString),
client: () => {
const stringStore01 = proxy<StringStore>({ value: initialString });
return { stringStore01, setString01: (value: StringStore['value']) => { stringStore01.value = value; } };
},
};
export const _valtioStores = {
valtioStart: valtioStartStore,
valtioNumber01: valtioNumberStore01,
valtioString01: valtioStringStore01,
};
Start.tsx
export default function ValtioStart() {
const { setStart } = useReactStore('valtioStart');
const raceStart = () => {
setStart('number');
};
return (
fieldset
button onClick={ raceStart }Start React Valtio!!!button
fieldset
)
}
Number01.tsx
export default function ValtioNumber01() {
const { startStore } = useReactStore('valtioStart');
const start = useSnapshot(startStore);
const { numberStore01, setNumber01 } = useReactStore('valtioNumber01');
const store01 = useSnapshot(numberStore01);
const { setNumber02 } = useReactStore('valtioNumber02');
useLayoutEffect(() => {
if (start.value !== 'number') return;
recordStart('ReactValtioNumber');
setNumber01(1);
}, [ start.value ]);
useLayoutEffect(() => {
if (!store01.value) return;
setNumber01(store01.value + 1);
if (store01.value === dragRaceMax) {
setNumber01(0);
setNumber02(1);
}
}, [ store01.value ]);
return null;
}
Number02.tsx
export default function ValtioNumber02() {
const { numberStore02, setNumber02 } = useReactStore('valtioNumber02');
const store02 = useSnapshot(numberStore02);
const { setNumber03 } = useReactStore('valtioNumber03');
useLayoutEffect(() => {
if (!store02.value) return;
setNumber02(store02.value + 1);
if (store02.value === dragRaceMax) {
setNumber02(0);
setNumber03(1);
}
}, [ store02.value ]);
return null;
}
Number10.tsx
export default function ValtioNumber10() {
const { numberStore10, setNumber10 } = useReactStore('valtioNumber10');
const store10 = useSnapshot(numberStore10);
const { setStart } = useReactStore('valtioStart');
useLayoutEffect(() => {
if (!store10.value) return;
if (store10.value === dragRaceMax) {
recordFinished('ReactValtioNumber');
setNumber10(0);
setStart('string');
} else {
setNumber10(store10.value + 1);
}
}, [ store10.value ]);
return null;
}
String01.tsx
export default function ValtioString01() {
const { startStore } = useReactStore('valtioStart');
const start = useSnapshot(startStore);
const { stringStore01, setString01 } = useReactStore('valtioString01');
const store01 = useSnapshot(stringStore01);
const { setString02 } = useReactStore('valtioString02');
useLayoutEffect(() => {
if (start.value !== 'string') return;
recordStart('ReactValtioString');
setString01('1');
}, [ start.value ]);
useLayoutEffect(() => {
const value = +store01.value;
if (!value) return;
setString01((value + 1).toString());
if (value === dragRaceMax) {
setString01('');
setString02('1');
}
}, [ store01.value ]);
return null;
}
String02.tsx
export default function ValtioString02() {
const { stringStore02, setString02 } = useReactStore('valtioString02');
const store02 = useSnapshot(stringStore02);
const { setString03 } = useReactStore('valtioString03');
useLayoutEffect(() => {
const value = +store02.value;
if (!value) return;
setString02((value + 1).toString());
if (value === dragRaceMax) {
setString02('');
setString03('1');
}
}, [ store02.value ]);
return null;
}
String10.tsx
export default function ValtioString10() {
const { stringStore10, setString10 } = useReactStore('valtioString10');
const store10 = useSnapshot(stringStore10);
const { setStart } = useReactStore('valtioStart');
useLayoutEffect(() => {
const value = +store10.value;
if (!value) return;
if (value === dragRaceMax) {
recordFinished('ReactValtioString');
logTotal('ReactValtio');
setStart(null);
setString10('');
} else {
setString10((value + 1).toString());
}
}, [ store10.value ]);
return null;
}
React Zustand
ReactZustand.astro
<ReactZustandStart client:only="react" />
<ReactZustandNumber01 client:only="react" />
<ReactZustandString01 client:only="react" />
ストア定義
type StartStore = 'number' | 'string' | null;
const initialStart = null as StartStore;
const startServerStore = () => ({
startStore: initialStart,
setStart: (_: StartStore) => {},
});
const zustandStartStore = {
server: startServerStore,
client: () => {
return create<ReturnType<typeof startServerStore>>(set => ({
startStore: initialStart,
setStart: (value: StartStore) => set({ startStore: value }),
}));
},
};
type NumberStore = number;
const initialNumber = 0 as NumberStore;
const numberServerStore01 = () => ({ numberStore01: initialNumber, setNumber01: (_: NumberStore) => {} });
const zustandNumberStore01 = {
server: numberServerStore01,
client: () => {
return create<ReturnType<typeof numberServerStore01>>(set => ({
numberStore01: initialNumber,
setNumber01: (value: NumberStore) => set({ numberStore01: value }),
}));
},
};
type StringStore = string;
const initialString = '' as StringStore;
const stringServerStore01 = () => ({ stringStore01: initialString, setString01: (_: StringStore) => {} });
const zustandStringStore01 = {
server: stringServerStore01,
client: () => {
return create<ReturnType<typeof stringServerStore01>>(set => ({
stringStore01: initialString,
setString01: (value: StringStore) => set({ stringStore01: value }),
}));
},
};
const zustandStores = {
zustandStart: zustandStartStore,
zustandNumber01: zustandNumberStore01,
zustandString01: zustandStringStore01,
} as const;
export type ZustandStores = typeof zustandStores;
export type ZustandStoreKeys = keyof ZustandStores;
const createServerStore = (key: ZustandStoreKeys): ZustandStores[typeof key]['server'] => zustandStores[key].server;
type CreateClientStore = ReturnType<ZustandStores[ZustandStoreKeys]['client']>;
const createClientStore = (key: ZustandStoreKeys): CreateClientStore => zustandStores[key].client();
type ClientStoreInstance = Record<ZustandStoreKeys, CreateClientStore | null>;
const clientStoreInstance = Object.fromEntries(Object.keys(zustandStores).map(key => ([ key, null ]))) as ClientStoreInstance;
export const useZustandStore = <K extends ZustandStoreKeys, R extends ReturnType<ZustandStores[K]['server']>>(key: K) => {
if (typeof window === 'undefined') return createServerStore(key) as R;
if (!clientStoreInstance[key]) {
clientStoreInstance[key] = createClientStore(key);
}
const useHook = clientStoreInstance[key]!;
return useHook() as R;
};
Start.tsx
export default function ZustandStart() {
const { setStart } = useZustandStore('zustandStart');
const raceStart = () => {
setStart('number');
};
return (
fieldset
button onClick={ raceStart }Start React Zustand!!!button
fieldset
)
}
Number01.tsx
export default function ZustandNumber01() {
const { startStore } = useZustandStore('zustandStart');
const { numberStore01, setNumber01 } = useZustandStore('zustandNumber01');
const { setNumber02 } = useZustandStore('zustandNumber02');
useLayoutEffect(() => {
if (startStore !== 'number') return;
recordStart('ReactZustandNumber');
setNumber01(1);
}, [ startStore ]);
useLayoutEffect(() => {
if (!numberStore01) return;
setNumber01(numberStore01 + 1);
if (numberStore01 === dragRaceMax) {
setNumber01(0);
setNumber02(1);
}
}, [ numberStore01 ]);
return null;
}
Number02.tsx
export default function ZustandNumber02() {
const { numberStore02, setNumber02 } = useZustandStore('zustandNumber02');
const { setNumber03 } = useZustandStore('zustandNumber03');
useLayoutEffect(() => {
if (!numberStore02) return;
setNumber02(numberStore02 + 1);
if (numberStore02 === dragRaceMax) {
setNumber02(0);
setNumber03(1);
}
}, [ numberStore02 ]);
return null;
}
Number10.tsx
export default function ZustandNumber10() {
const { numberStore10, setNumber10 } = useZustandStore('zustandNumber10');
const { setStart } = useZustandStore('zustandStart');
useLayoutEffect(() => {
if (!numberStore10) return;
if (numberStore10 === dragRaceMax) {
recordFinished('ReactZustandNumber');
setNumber10(0);
setStart('string');
} else {
setNumber10(numberStore10 + 1);
}
}, [ numberStore10 ]);
return null;
}
String01.tsx
export default function ZustandString01() {
const { startStore } = useZustandStore('zustandStart');
const { stringStore01, setString01 } = useZustandStore('zustandString01');
const { setString02 } = useZustandStore('zustandString02');
useLayoutEffect(() => {
if (startStore !== 'string') return;
recordStart('ReactZustandString');
setString01('1');
}, [ startStore ]);
useLayoutEffect(() => {
const value = +stringStore01;
if (!value) return;
setString01((value + 1).toString());
if (value === dragRaceMax) {
setString01('');
setString02('1');
}
}, [ stringStore01 ]);
return null;
}
String02.tsx
export default function ZustandString02() {
const { stringStore02, setString02 } = useZustandStore('zustandString02');
const { setString03 } = useZustandStore('zustandString03');
useLayoutEffect(() => {
const value = +stringStore02;
if (!value) return;
setString02((value + 1).toString());
if (value === dragRaceMax) {
setString02('');
setString03('1');
}
}, [ stringStore02 ]);
return null;
}
String10.tsx
export default function ZustandString10() {
const { stringStore10, setString10 } = useZustandStore('zustandString10');
const { setStart } = useZustandStore('zustandStart');
useLayoutEffect(() => {
const value = +stringStore10;
if (!value) return;
if (value === dragRaceMax) {
recordFinished('ReactZustandString');
logTotal('ReactZustand');
setStart(null);
setString10('');
} else {
setString10((value + 1).toString());
}
}, [ stringStore10 ]);
return null;
}
React Nano Stores
ReactNano.astro
<ReactNanoStart client:only="react" />
<ReactNanoNumber01 client:only="react" />
...
<ReactNanoString01 client:only="react" />
...
ストア定義
Solid Nano Stores と同一です。
Start.tsx
export default function NanoStart() {
const { $startStore } = useReactStore('nanoStart');
const raceStart = () => {
$startStore.set('number');
};
return (
fieldset
button onClick={ raceStart }Start React Nano Stores!!!button
fieldset
)
}
Number01.tsx
export default function NanoNumber01() {
const { $startStore } = useReactStore('nanoStart');
const start = useStore($startStore, { ssr: 'initial' });
const { $numberStore01 } = useReactStore('nanoNumber01');
const store01 = useStore($numberStore01, { ssr: 'initial' });
const { $numberStore02 } = useReactStore('nanoNumber02');
useLayoutEffect(() => {
if (start !== 'number') return;
recordStart('ReactNanoNumber');
$numberStore01.set(1);
}, [ start ]);
useLayoutEffect(() => {
if (!store01) return;
$numberStore01.set(store01 + 1);
if (store01 === dragRaceMax) {
$numberStore01.set(0);
$numberStore02.set(1);
}
}, [ store01 ]);
return null;
}
Number02.tsx
export default function NanoNumber02() {
const { $numberStore02 } = useReactStore('nanoNumber02');
const store02 = useStore($numberStore02, { ssr: 'initial' });
const { $numberStore03 } = useReactStore('nanoNumber03');
useLayoutEffect(() => {
if (!store02) return;
$numberStore02.set(store02 + 1);
if (store02 === dragRaceMax) {
$numberStore02.set(0);
$numberStore03.set(1);
}
}, [ store02 ]);
return null;
}
Number10.tsx
export default function NanoNumber10() {
const { $numberStore10 } = useReactStore('nanoNumber10');
const store10 = useStore($numberStore10, { ssr: 'initial' });
const { $startStore } = useReactStore('nanoStart');
useLayoutEffect(() => {
if (!store10) return;
if (store10 === dragRaceMax) {
recordFinished('ReactNanoNumber');
$numberStore10.set(0);
$startStore.set('string');
} else {
$numberStore10.set(store10 + 1);
}
}, [ store10 ]);
return null;
}
String01.tsx
export default function NanoString01() {
const { $startStore } = useReactStore('nanoStart');
const start = useStore($startStore, { ssr: 'initial' });
const { $stringStore01 } = useReactStore('nanoString01');
const store01 = useStore($stringStore01, { ssr: 'initial' });
const { $stringStore02 } = useReactStore('nanoString02');
useLayoutEffect(() => {
if (start !== 'string') return;
recordStart('ReactNanoString');
$stringStore01.set('1');
}, [ start ]);
useLayoutEffect(() => {
const value = +store01;
if (!value) return;
$stringStore01.set((value + 1).toString());
if (value === dragRaceMax) {
$stringStore01.set('');
$stringStore02.set('1');
}
}, [ store01 ]);
return null;
}
String02.tsx
export default function NanoString02() {
const { $stringStore02 } = useReactStore('nanoString02');
const store02 = useStore($stringStore02, { ssr: 'initial' });
const { $stringStore03 } = useReactStore('nanoString03');
useLayoutEffect(() => {
const value = +store02;
if (!value) return;
$stringStore02.set((value + 1).toString());
if (value === dragRaceMax) {
$stringStore02.set('');
$stringStore03.set('1');
}
}, [ store02 ]);
return null;
}
String10.tsx
export default function NanoString10() {
const { $stringStore10 } = useReactStore('nanoString10');
const store10 = useStore($stringStore10, { ssr: 'initial' });
const { $startStore } = useReactStore('nanoStart');
useLayoutEffect(() => {
const value = +store10;
if (!value) return;
if (value === dragRaceMax) {
recordFinished('ReactNanoString');
logTotal('ReactNano');
$startStore.set(null);
$stringStore10.set('');
} else {
$stringStore10.set((value + 1).toString());
}
}, [ store10 ]);
return null;
}
計測結果
ウォームアップ前である リロード直後は除き、それ以降の計測値です。
Solid Signal
dragRaceMax = 2
1 回目
- SolidSignalNumber: 0 ミリ秒
- SolidSignalString: 0.09999996423721313 ミリ秒
- SolidSignal: 0.09999996423721313 ミリ秒
2 回目
- SolidSignalNumber: 0 ミリ秒
- SolidSignalString: 0.10000002384185791 ミリ秒
- SolidSignal: 0.10000002384185791 ミリ秒
3 回目
- SolidSignalNumber: 0.09999996423721313 ミリ秒
- SolidSignalString: 0.10000002384185791 ミリ秒
- SolidSignal: 0.19999998807907104 ミリ秒
dragRaceMax = 10
1 回目
- SolidSignalNumber: 0.19999998807907104 ミリ秒
- SolidSignalString: 0 ミリ秒
- SolidSignal: 0.19999998807907104 ミリ秒
2 回目
- SolidSignalNumber: 0.19999998807907104 ミリ秒
- SolidSignalString: 0.10000002384185791 ミリ秒
- SolidSignal: 0.30000001192092896 ミリ秒
3 回目
- SolidSignalNumber: 0 ミリ秒
- SolidSignalString: 0.20000004768371582 ミリ秒
- SolidSignal: 0.20000004768371582 ミリ秒
dragRaceMax = 50
1 回目
- SolidSignalNumber: 0.3999999761581421 ミリ秒
- SolidSignalString: 0.20000004768371582 ミリ秒
- SolidSignal: 0.6000000238418579 ミリ秒
2 回目
- SolidSignalNumber: 0.3999999761581421 ミリ秒
- SolidSignalString: 0.40000003576278687 ミリ秒
- SolidSignal: 0.800000011920929 ミリ秒
3 回目
- SolidSignalNumber: 0.19999998807907104 ミリ秒
- SolidSignalString: 0.3999999761581421 ミリ秒
- SolidSignal: 0.5999999642372131 ミリ秒
完走平均
- dragRaceMax = 2: 0.1333333254 ミリ秒
- dragRaceMax = 10: 0.2333333492 ミリ秒
- dragRaceMax = 50: 0.6666666667 ミリ秒
Solid Nano Stores
dragRaceMax = 2
1 回目
- SolidNanoNumber: 0.19999998807907104 ミリ秒
- SolidNanoString: 0.19999998807907104 ミリ秒
- SolidNano: 0.3999999761581421 ミリ秒
2 回目
- SolidNanoNumber: 0.10000002384185791 ミリ秒
- SolidNanoString: 0.19999998807907104 ミリ秒
- SolidNano: 0.30000001192092896 ミリ秒
3 回目
- SolidNanoNumber: 0 ミリ秒
- SolidNanoString: 0.19999998807907104 ミリ秒
- SolidNano: 0.19999998807907104 ミリ秒
dragRaceMax = 10
1 回目
- SolidNanoNumber: 0.3999999761581421 ミリ秒
- SolidNanoString: 0.30000001192092896 ミリ秒
- SolidNano: 0.699999988079071 ミリ秒
2 回目
- SolidNanoNumber: 0.3999999761581421 ミリ秒
- SolidNanoString: 0.19999998807907104 ミリ秒
- SolidNano: 0.5999999642372131 ミリ秒
3 回目
- SolidNanoNumber: 0.19999998807907104 ミリ秒
- SolidNanoString: 0.10000002384185791 ミリ秒
- SolidNano: 0.30000001192092896 ミリ秒
dragRaceMax = 50
1 回目
- SolidNanoNumber: 1 ミリ秒
- SolidNanoString: 0.699999988079071 ミリ秒
- SolidNano: 1.699999988079071 ミリ秒
2 回目
- SolidNanoNumber: 1.100000023841858 ミリ秒
- SolidNanoString: 0.6000000238418579 ミリ秒
- SolidNano: 1.7000000476837158 ミリ秒
3 回目
- SolidNanoNumber: 0.8999999761581421 ミリ秒
- SolidNanoString: 0.7000000476837158 ミリ秒
- SolidNano: 1.600000023841858 ミリ秒
完走平均
- dragRaceMax = 2: 0.2999999921 ミリ秒
- dragRaceMax = 10: 0.5333333214 ミリ秒
- dragRaceMax = 50: 1.6666666865 ミリ秒
Svelte Runes
dragRaceMax = 2
1 回目
- SvelteRunesNumber: 0.20000004768371582 ミリ秒
- SvelteRunesString: 0.19999998807907104 ミリ秒
- SvelteRunes: 0.40000003576278687 ミリ秒
2 回目
- SvelteRunesNumber: 0.30000001192092896 ミリ秒
- SvelteRunesString: 0.40000003576278687 ミリ秒
- SvelteRunes: 0.7000000476837158 ミリ秒
3 回目
- SvelteRunesNumber: 0.19999998807907104 ミリ秒
- SvelteRunesString: 0.19999998807907104 ミリ秒
- SvelteRunes: 0.3999999761581421 ミリ秒
dragRaceMax = 10
1 回目
- SvelteRunesNumber: 16.600000023841858 ミリ秒
- SvelteRunesString: 41.69999998807907 ミリ秒
- SvelteRunes: 58.30000001192093 ミリ秒
2 回目
- SvelteRunesNumber: 12.900000035762787 ミリ秒
- SvelteRunesString: 37.5 ミリ秒
- SvelteRunes: 50.40000003576279 ミリ秒
3 回目
- SvelteRunesNumber: 12.5 ミリ秒
- SvelteRunesString: 37.09999996423721 ミリ秒
- SvelteRunes: 49.59999996423721 ミリ秒
dragRaceMax = 50
1 回目
- SvelteRunesNumber: 351.69999998807907 ミリ秒
- SvelteRunesString: 759.8000000119209 ミリ秒
- SvelteRunes: 1111.5 ミリ秒
2 回目
- SvelteRunesNumber: 334.39999997615814 ミリ秒
- SvelteRunesString: 754.9000000357628 ミリ秒
- SvelteRunes: 1089.300000011921 ミリ秒
3 回目
- SvelteRunesNumber: 338.9000000357628 ミリ秒
- SvelteRunesString: 761.8999999761581 ミリ秒
- SvelteRunes: 1100.800000011921 ミリ秒
完走平均
- dragRaceMax = 2: 0.5000000199 ミリ秒
- dragRaceMax = 10: 52.7666666706 ミリ秒
- dragRaceMax = 50: 1,100.5333333413 ミリ秒
Svelte Nano Stores
dragRaceMax = 2
1 回目
- SvelteNanoNumber: 0.3999999761581421 ミリ秒
- SvelteNanoString: 0.40000003576278687 ミリ秒
- SvelteNano: 0.800000011920929 ミリ秒
2 回目
- SvelteNanoNumber: 0.19999998807907104 ミリ秒
- SvelteNanoString: 0.3999999761581421 ミリ秒
- SvelteNano: 0.5999999642372131 ミリ秒
3 回目
- SvelteNanoNumber: 0.3999999761581421 ミリ秒
- SvelteNanoString: 0.5 ミリ秒
- SvelteNano: 0.8999999761581421 ミリ秒
dragRaceMax = 10
1 回目
- SvelteNanoNumber: 15.699999988079071 ミリ秒
- SvelteNanoString: 41.60000002384186 ミリ秒
- SvelteNano: 57.30000001192093 ミリ秒
2 回目
- SvelteNanoNumber: 12.799999952316284 ミリ秒
- SvelteNanoString: 37.19999998807907 ミリ秒
- SvelteNano: 49.999999940395355 ミリ秒
3 回目
- SvelteNanoNumber: 13.5 ミリ秒
- SvelteNanoString: 37.30000001192093 ミリ秒
- SvelteNano: 50.80000001192093 ミリ秒
dragRaceMax = 50
1 回目
- SvelteNanoNumber: 353.0999999642372 ミリ秒
- SvelteNanoString: 758.6999999880791 ミリ秒
- SvelteNano: 1111.7999999523163 ミリ秒
2 回目
- SvelteNanoNumber: 338.80000001192093 ミリ秒
- SvelteNanoString: 744.1999999880791 ミリ秒
- SvelteNano: 1083 ミリ秒
3 回目
- SvelteNanoNumber: 334.80000001192093 ミリ秒
- SvelteNanoString: 745.5 ミリ秒
- SvelteNano: 1080.300000011921 ミリ秒
完走平均
- dragRaceMax = 2: 0.7666666508 ミリ秒
- dragRaceMax = 10: 52.6999999881 ミリ秒
- dragRaceMax = 50: 1,091.6999999881 ミリ秒
Vue Ref
dragRaceMax = 2
1 回目
- VueRefNumber: 0.09999996423721313 ミリ秒
- VueRefString: 0.10000002384185791 ミリ秒
- VueRef: 0.19999998807907104 ミリ秒
2 回目
- VueRefNumber: 0.10000002384185791 ミリ秒
- VueRefString: 0.19999998807907104 ミリ秒
- VueRef: 0.30000001192092896 ミリ秒
3 回目
- VueRefNumber: 0.20000004768371582 ミリ秒
- VueRefString: 0 ミリ秒
- VueRef: 0.20000004768371582 ミリ秒
dragRaceMax = 10
1 回目
- VueRefNumber: 0.30000001192092896 ミリ秒
- VueRefString: 0.30000001192092896 ミリ秒
- VueRef: 0.6000000238418579 ミリ秒
2 回目
- VueRefNumber: 0.19999998807907104 ミリ秒
- VueRefString: 0.19999998807907104 ミリ秒
- VueRef: 0.3999999761581421 ミリ秒
3 回目
- VueRefNumber: 0.30000001192092896 ミリ秒
- VueRefString: 0.10000002384185791 ミリ秒
- VueRef: 0.40000003576278687 ミリ秒
dragRaceMax = 50
1 回目
- VueRefNumber: 0.3999999761581421 ミリ秒
- VueRefString: 0.3999999761581421 ミリ秒
- VueRef: 0.7999999523162842 ミリ秒
2 回目
- VueRefNumber: 0.19999998807907104 ミリ秒
- VueRefString: 0.19999998807907104 ミリ秒
- VueRef: 0.3999999761581421 ミリ秒
3 回目
- VueRefNumber: 0.20000004768371582 ミリ秒
- VueRefString: 0.3999999761581421 ミリ秒
- VueRef: 0.6000000238418579 ミリ秒
完走平均
- dragRaceMax = 2: 0.2333333492 ミリ秒
- dragRaceMax = 10: 0.4666666786 ミリ秒
- dragRaceMax = 50: 0.5999999841 ミリ秒
Vue Nano Stores
dragRaceMax = 2
1 回目
- VueNanoNumber: 0.10000002384185791 ミリ秒
- VueNanoString: 0.19999998807907104 ミリ秒
- VueNano: 0.30000001192092896 ミリ秒
2 回目
- VueNanoNumber: 0.09999996423721313 ミリ秒
- VueNanoString: 0.19999998807907104 ミリ秒
- VueNano: 0.2999999523162842 ミリ秒
3 回目
- VueNanoNumber: 0.19999998807907104 ミリ秒
- VueNanoString: 0.19999998807907104 ミリ秒
- VueNano: 0.3999999761581421 ミリ秒
dragRaceMax = 10
1 回目
- VueNanoNumber: 0.30000001192092896 ミリ秒
- VueNanoString: 0.30000001192092896 ミリ秒
- VueNano: 0.6000000238418579 ミリ秒
2 回目
- VueNanoNumber: 0.09999996423721313 ミリ秒
- VueNanoString: 0.10000002384185791 ミリ秒
- VueNano: 0.19999998807907104 ミリ秒
3 回目
- VueNanoNumber: 0.10000002384185791 ミリ秒
- VueNanoString: 0.19999998807907104 ミリ秒
- VueNano: 0.30000001192092896 ミリ秒
dragRaceMax = 50
1 回目
- VueNanoNumber: 0.5999999642372131 ミリ秒
- VueNanoString: 0.800000011920929 ミリ秒
- VueNano: 1.399999976158142 ミリ秒
2 回目
- VueNanoNumber: 0.699999988079071 ミリ秒
- VueNanoString: 0.5999999642372131 ミリ秒
- VueNano: 1.2999999523162842 ミリ秒
3 回目
- VueNanoNumber: 0.7000000476837158 ミリ秒
- VueNanoString: 0.5 ミリ秒
- VueNano: 1.2000000476837158 ミリ秒
完走平均
- dragRaceMax = 2: 0.3333333135 ミリ秒
- dragRaceMax = 10: 0.3666666746 ミリ秒
- dragRaceMax = 50: 1.2999999921 ミリ秒
React uSES
dragRaceMax = 2
1 回目
- ReactUsesNumber: 2.399999976158142 ミリ秒
- ReactUsesString: 1.800000011920929 ミリ秒
- ReactUses: 4.199999988079071 ミリ秒
2 回目
- ReactUsesNumber: 1.399999976158142 ミリ秒
- ReactUsesString: 1.5 ミリ秒
- ReactUses: 2.899999976158142 ミリ秒
3 回目
- ReactUsesNumber: 1.199999988079071 ミリ秒
- ReactUsesString: 1.199999988079071 ミリ秒
- ReactUses: 2.399999976158142 ミリ秒
dragRaceMax = 10
1 回目
- ReactUsesNumber: 5.599999964237213 ミリ秒
- ReactUsesString: 4.5 ミリ秒
- ReactUses: 10.099999964237213 ミリ秒
2 回目
- ReactUsesNumber: 4.199999988079071 ミリ秒
- ReactUsesString: 3.399999976158142 ミリ秒
- ReactUses: 7.599999964237213 ミリ秒
3 回目
- ReactUsesNumber: 3.299999952316284 ミリ秒
- ReactUsesString: 3.100000023841858 ミリ秒
- ReactUses: 6.399999976158142 ミリ秒
dragRaceMax = 50
1 回目
- ReactUsesNumber: 16.5 ミリ秒
- ReactUsesString: 15.800000011920929 ミリ秒
- ReactUses: 32.30000001192093 ミリ秒
2 回目
- ReactUsesNumber: 15.800000011920929 ミリ秒
- ReactUsesString: 15.5 ミリ秒
- ReactUses: 31.30000001192093 ミリ秒
3 回目
- ReactUsesNumber: 23.5 ミリ秒
- ReactUsesString: 16.299999952316284 ミリ秒
- ReactUses: 39.799999952316284 ミリ秒
完走平均
- dragRaceMax = 2: 3.1666666468 ミリ秒
- dragRaceMax = 10: 8.03333330154 ミリ秒
- dragRaceMax = 50: 34.4666666587 ミリ秒
React Jotai
dragRaceMax = 2
1 回目
- ReactJotaiNumber: 3.099999964237213 ミリ秒
- ReactJotaiString: 2.199999988079071 ミリ秒
- ReactJotai: 5.299999952316284 ミリ秒
2 回目
- ReactJotaiNumber: 2.699999988079071 ミリ秒
- ReactJotaiString: 1.899999976158142 ミリ秒
- ReactJotai: 4.599999964237213 ミリ秒
3 回目
- ReactJotaiNumber: 2.200000047683716 ミリ秒
- ReactJotaiString: 1.600000023841858 ミリ秒
- ReactJotai: 3.8000000715255737 ミリ秒
dragRaceMax = 10
1 回目
- ReactJotaiNumber: 6.100000023841858 ミリ秒
- ReactJotaiString: 5 ミリ秒
- ReactJotai: 11.100000023841858 ミリ秒
2 回目
- ReactJotaiNumber: 6.199999988079071 ミリ秒
- ReactJotaiString: 5.600000023841858 ミリ秒
- ReactJotai: 11.800000011920929 ミリ秒
3 回目
- ReactJotaiNumber: 6.900000035762787 ミリ秒
- ReactJotaiString: 5.300000011920929 ミリ秒
- ReactJotai: 12.200000047683716 ミリ秒
dragRaceMax = 50
1 回目
- ReactJotaiNumber: 25.100000023841858 ミリ秒
- ReactJotaiString: 26.5 ミリ秒
- ReactJotai: 51.60000002384186 ミリ秒
2 回目
- ReactJotaiNumber: 23.399999976158142 ミリ秒
- ReactJotaiString: 23.700000047683716 ミリ秒
- ReactJotai: 47.10000002384186 ミリ秒
3 回目
- ReactJotaiNumber: 24.30000001192093 ミリ秒
- ReactJotaiString: 22.5 ミリ秒
- ReactJotai: 46.80000001192093 ミリ秒
完走平均
- dragRaceMax = 2: 4.5666666627 ミリ秒
- dragRaceMax = 10: 11.7000000278 ミリ秒
- dragRaceMax = 50: 48.5000000199 ミリ秒
React Legend State
dragRaceMax = 2
1 回目
- ReactLegendNumber: 1 ミリ秒
- ReactLegendString: 1.300000011920929 ミリ秒
- ReactLegend: 2.300000011920929 ミリ秒
2 回目
- ReactLegendNumber: 0.7999999523162842 ミリ秒
- ReactLegendString: 1 ミリ秒
- ReactLegend: 1.7999999523162842 ミリ秒
3 回目
- ReactLegendNumber: 0.6000000238418579 ミリ秒
- ReactLegendString: 0.800000011920929 ミリ秒
- ReactLegend: 1.4000000357627869 ミリ秒
dragRaceMax = 10
1 回目
- ReactLegendNumber: 5.700000047683716 ミリ秒
- ReactLegendString: 9.699999988079071 ミリ秒
- ReactLegend: 15.400000035762787 ミリ秒
2 回目
- ReactLegendNumber: 5.400000035762787 ミリ秒
- ReactLegendString: 9.399999976158142 ミリ秒
- ReactLegend: 14.800000011920929 ミリ秒
3 回目
- ReactLegendNumber: 5.199999988079071 ミリ秒
- ReactLegendString: 9.5 ミリ秒
- ReactLegend: 14.699999988079071 ミリ秒
dragRaceMax = 50
1 回目
- ReactLegendNumber: 46.19999998807907 ミリ秒
- ReactLegendString: 53.40000003576279 ミリ秒
- ReactLegend: 99.60000002384186 ミリ秒
2 回目
- ReactLegendNumber: 46.69999998807907 ミリ秒
- ReactLegendString: 53 ミリ秒
- ReactLegend: 99.69999998807907 ミリ秒
3 回目
- ReactLegendNumber: 45.30000001192093 ミリ秒
- ReactLegendString: 55.30000001192093 ミリ秒
- ReactLegend: 100.60000002384186 ミリ秒
完走平均
- dragRaceMax = 2: 1.8333333333 ミリ秒
- dragRaceMax = 10: 14.9666666786 ミリ秒
- dragRaceMax = 50: 99.9666666786 ミリ秒
React Valtio
dragRaceMax = 2
1 回目
- ReactValtioNumber: 3 ミリ秒
- ReactValtioString: 2.600000023841858 ミリ秒
- ReactValtio: 5.600000023841858 ミリ秒
2 回目
- ReactValtioNumber: 3.199999988079071 ミリ秒
- ReactValtioString: 2.199999988079071 ミリ秒
- ReactValtio: 5.399999976158142 ミリ秒
3 回目
- ReactValtioNumber: 2.600000023841858 ミリ秒
- ReactValtioString: 2 ミリ秒
- ReactValtio: 4.600000023841858 ミリ秒
dragRaceMax = 10
1 回目
- ReactValtioNumber: 3.699999988079071 ミリ秒
- ReactValtioString: 2.899999976158142 ミリ秒
- ReactValtio: 6.599999964237213 ミリ秒
2 回目
- ReactValtioNumber: 3.699999988079071 ミリ秒
- ReactValtioString: 2.800000011920929 ミリ秒
- ReactValtio: 6.5 ミリ秒
3 回目
- ReactValtioNumber: 3.300000011920929 ミリ秒
- ReactValtioString: 3.100000023841858 ミリ秒
- ReactValtio: 6.400000035762787 ミリ秒
dragRaceMax = 50
1 回目
- ReactValtioNumber: 12.899999976158142 ミリ秒
- ReactValtioString: 10.899999976158142 ミリ秒
- ReactValtio: 23.799999952316284 ミリ秒
2 回目
- ReactValtioNumber: 13 ミリ秒
- ReactValtioString: 12.199999988079071 ミリ秒
- ReactValtio: 25.19999998807907 ミリ秒
3 回目
- ReactValtioNumber: 11.699999988079071 ミリ秒
- ReactValtioString: 10.100000023841858 ミリ秒
- ReactValtio: 21.80000001192093 ミリ秒
完走平均
- dragRaceMax = 2: 5.2000000079 ミリ秒
- dragRaceMax = 10: 6.5 ミリ秒
- dragRaceMax = 50: 23.5999999841 ミリ秒
React Zustand
dragRaceMax = 2
1 回目
- ReactZustandNumber: 2.300000011920929 ミリ秒
- ReactZustandString: 1.899999976158142 ミリ秒
- ReactZustand: 4.199999988079071 ミリ秒
2 回目
- ReactZustandNumber: 3.300000011920929 ミリ秒
- ReactZustandString: 3.100000023841858 ミリ秒
- ReactZustand: 6.400000035762787 ミリ秒
3 回目
- ReactZustandNumber: 1.899999976158142 ミリ秒
- ReactZustandString: 1.600000023841858 ミリ秒
- ReactZustand: 3.5 ミリ秒
dragRaceMax = 10
1 回目
- ReactZustandNumber: 4.299999952316284 ミリ秒
- ReactZustandString: 4.300000011920929 ミリ秒
- ReactZustand: 8.599999964237213 ミリ秒
2 回目
- ReactZustandNumber: 5.100000023841858 ミリ秒
- ReactZustandString: 4.199999988079071 ミリ秒
- ReactZustand: 9.300000011920929 ミリ秒
3 回目
- ReactZustandNumber: 4.900000035762787 ミリ秒
- ReactZustandString: 4.100000023841858 ミリ秒
- ReactZustand: 9.000000059604645 ミリ秒
dragRaceMax = 50
1 回目
- ReactZustandNumber: 26.100000023841858 ミリ秒
- ReactZustandString: 19.399999976158142 ミリ秒
- ReactZustand: 45.5 ミリ秒
2 回目
- ReactZustandNumber: 21 ミリ秒
- ReactZustandString: 19.80000001192093 ミリ秒
- ReactZustand: 40.80000001192093 ミリ秒
3 回目
- ReactZustandNumber: 26.599999964237213 ミリ秒
- ReactZustandString: 19.5 ミリ秒
- ReactZustand: 46.09999996423721 ミリ秒
完走平均
- dragRaceMax = 2: 4.7000000079 ミリ秒
- dragRaceMax = 10: 8.9666666786 ミリ秒
- dragRaceMax = 50: 44.1333333254 ミリ秒
React Nano Stores
dragRaceMax = 2
1 回目
- ReactNanoNumber: 2.299999952316284 ミリ秒
- ReactNanoString: 1.800000011920929 ミリ秒
- ReactNano: 4.099999964237213 ミリ秒
2 回目
- ReactNanoNumber: 2 ミリ秒
- ReactNanoString: 1.5999999642372131 ミリ秒
- ReactNano: 3.599999964237213 ミリ秒
3 回目
- ReactNanoNumber: 2 ミリ秒
- ReactNanoString: 1.5 ミリ秒
- ReactNano: 3.5 ミリ秒
dragRaceMax = 10
1 回目
- ReactNanoNumber: 5 ミリ秒
- ReactNanoString: 3.5 ミリ秒
- ReactNano: 8.5 ミリ秒
2 回目
- ReactNanoNumber: 4.399999976158142 ミリ秒
- ReactNanoString: 3.400000035762787 ミリ秒
- ReactNano: 7.800000011920929 ミリ秒
3 回目
- ReactNanoNumber: 6 ミリ秒
- ReactNanoString: 4.600000023841858 ミリ秒
- ReactNano: 10.600000023841858 ミリ秒
dragRaceMax = 50
1 回目
- ReactNanoNumber: 16.30000001192093 ミリ秒
- ReactNanoString: 15.900000035762787 ミリ秒
- ReactNano: 32.200000047683716 ミリ秒
2 回目
- ReactNanoNumber: 17.400000035762787 ミリ秒
- ReactNanoString: 17.900000035762787 ミリ秒
- ReactNano: 35.300000071525574 ミリ秒
3 回目
- ReactNanoNumber: 21.400000035762787 ミリ秒
- ReactNanoString: 17 ミリ秒
- ReactNano: 38.40000003576279 ミリ秒
完走平均
- dragRaceMax = 2: 3.7333333095 ミリ秒
- dragRaceMax = 10: 8.9666666786 ミリ秒
- dragRaceMax = 50: 35.3000000517 ミリ秒
平均値ランキング
dragRaceMax = 2
- SolidSignal: 0.1333333254 ミリ秒
- VueRef: 0.2333333492 ミリ秒
- SolidNanoStores: 0.2999999921 ミリ秒
- VueNanoStores: 0.3333333135 ミリ秒
- SvelteRunes: 0.5000000199 ミリ秒
- SvelteNanoStores: 0.7666666508 ミリ秒
- ReactLegendState: 1.8333333333 ミリ秒
- ReactUses: 3.1666666468 ミリ秒
- ReactNanoStores: 3.7333333095 ミリ秒
- ReactJotai: 4.5666666627 ミリ秒
- ReactZustand: 4.7000000079 ミリ秒
- ReactValtio: 5.2000000079 ミリ秒
dragRaceMax = 10
- SolidSignal: 0.2333333492 ミリ秒
- VueNanoStores: 0.3666666746 ミリ秒
- VueRef: 0.4666666786 ミリ秒
- SolidNanoStores: 0.5333333214 ミリ秒
- ReactValtio: 6.5 ミリ秒
- ReactUses: 8.03333330154 ミリ秒
- ReactZustand / ReactNanoStores: 8.9666666786 ミリ秒
- ReactJotai: 11.7000000278 ミリ秒
- ReactLegendState: 14.9666666786 ミリ秒
- SvelteNanoStores: 52.6999999881 ミリ秒
- SvelteRunes: 52.7666666706 ミリ秒
dragRaceMax = 50
- VueRef: 0.5999999841 ミリ秒
- SolidSignal: 0.6666666667 ミリ秒
- VueNanoStores: 1.2999999921 ミリ秒
- SolidNanoStores: 1.6666666865 ミリ秒
- ReactValtio: 23.5999999841 ミリ秒
- ReactUses: 34.4666666587 ミリ秒
- ReactNanoStores: 35.3000000517 ミリ秒
- ReactZustand: 44.1333333254 ミリ秒
- ReactJotai: 48.5000000199 ミリ秒
- ReactLegendState: 99.9666666786 ミリ秒
- SvelteRunes: 1,100.5333333413 ミリ秒
- SvelteNanoStores: 1,091.6999999881 ミリ秒
今回も 3 回ずつの結果とは言え、有意な数値とは言えませんが、この結果になりました。
実に興味深い結果です。
もしかしたら、Svelte は、「この $effect() は、コンパイル時とちゃんと合ってるか?」ってのを毎回検証して、連鎖数が増えるほど、どんどん遅くなってる気がします。気がするだけで、実際どうかは分かりませんし、普通こんな極限状態で連鎖させません。
あと React Valtio は、少ない連鎖数の場合は最下位ですが、連鎖数が増えると React 内での最上位を守っていたり、逆に少ない連鎖数で LegendState は React 内で最上位だったのに、連鎖数が増えるとどんどん遅くなっていたのが驚きでした。
今度こそ、閉幕にしたいです。