שליטה על Input ב React לעומת Vue
הקוד הבא ב Vue יכול להטעות:
<script setup lang="ts">
import {ref} from 'vue';
const data = ref('a');
function handleInput(e: any) {
if (Math.random() * 10 < 2) {
data.value = e.target.value;
}
}
</script>
<template>
<input type="text" @input="handleInput" :value="data" />
</template>
במיוחד אם משווים אותו לקוד מקביל בריאקט:
function App() {
const [data, setData] = useState('a');
function handleInput(e) {
if (Math.random() * 10 < 2) {
setData(e.target.value);
}
}
return <input type="text" value={data} onChange={handleInput} />
}
גירסת הריאקט תעדכן את הטקסט בתיבה רק ב 20% מהמקרים. בשאר המקרים גם המשתנה data וגם תיבת הטקסט שמופיעה על המסך יישארו מסונכרנים ויציגו את הטקסט הישן. ריאקט יתעלם מהלחיצות.
בגירסת ה vue ההתנהגות שונה: אנחנו עדיין נכנסים לפונקציה ועדיין רק ב 20% מהמקרים נכנסים לבלוק לביצוע של התנאי, אבל הטקסט בתיבה תמיד ישתנה - בלי קשר למצב של המשתנה data. נשים לב ששינוי של data ממקום אחר עדיין יגרום לעדכון של תיבת הטקסט, כלומר בגירסת ה vue אני יכול להוסיף כפתור:
<input type="text" @input="handleInput" :value="data" />
<button @click="data = ''">Reset</button>
ואז לחיצות על הכפתור יאפסו את המשתנה וגם את הטקסט בתיבה. המנגנון הריאקטיבי של Vue אומר שכשיש שינוי במשתנה גם תיבת הטקסט על המסך תעודכן, אבל כשאין שינוי במשתנה לתיבה לא יקרה כלום.
גם הגדרת v-model עם אפשרות לכתיבה תישאר עם אותה התנהגות מבלבלת:
<script setup lang="ts">
import {computed, ref} from 'vue';
const data = ref('a');
const dataModel = computed({
get() {
return data.value;
},
set(newValue) {
if (Math.random() * 10 < 2) {
data.value = newValue;
}
}
})
</script>
<template>
<Todos />
<input type="text" v-model="dataModel" />
<button @click="data = ''"></button>
</template>
גם במצב זה ב 20% מהמקרים תהיה כתיבה למשתנה אבל בשאר 80% לא תהיה כתיבה למשתנה והטקסט בתיבת הטקסט יצא מסינכרון, התיבה תציג את הטקסט שנכתב אבל המשתנה לא יכיל אותו.
מה בכל זאת אפשר לעשות? אם אתם רוצים את ההתנהגות של ריאקט תצטרכו לכתוב אותה בעצמכם:
function handleInput(e) {
if (Math.random() * 10 < 2) {
data.value = e.target.value;
} else {
e.target.value = data.value;
}
}