为什么在这种情况下不能使用props来响应式地处理数据?

huangapple go评论62阅读模式
英文:

Why isn't reactive the data with props in this situation?

问题

我在`Desks.vue`组件中有两个组件,`Rooms.vue`和`Desks.vue`,除了`App.vue`组件。
当我在`Rooms.vue`组件中点击一个滑动元素时,会打开`Desks.vue`组件并将`room`对象传递到`App.vue`,然后传递到`Desks.vue`作为属性。在`Desks.vue`中,属性`room`是响应式的,所以当我点击另一个滑动元素时,`prop.room`会发生变化。然而,在`reserveData`中,`room`属性没有变化。

你能告诉我为什么在`reserveData`对象中`room`属性不是响应式的吗?
英文:

I have two componets beside the App.vue component, the Rooms.vue and the Desks.vue.
When I click on the on one of the swiper element in the Rooms.vue component, then opens the Desks.vue component and emits the room object to App.vue, and from there is passed down into the Desks.vue as prop. In the Desks.vue the prop room is reactive, so when I click on another swiper element, the prop.room is changed. However, in the reserveData the room property doesn't change.

Can you tell me why isn't reactive the room prop in the reserveData object?

App.vue

const room = ref(null)
const openRoom = (rm) => {
  room.value = rm
  console.log(room.value)
}

const closeRoom = () => {
  room.value = null
}

</script>

<template>
  <Header title="Friendbook"/>
  <Rooms @openRoom="openRoom" :room="room"/>
  <transition>
    <Desks v-if="room" :room="room" @closeRoom="closeRoom"/>
  </transition>
</template>

Rooms.vue

const emit = defineEmits(['openRoom'])
const props = defineProps({room: Object})

const roomStore = useRoomStore()

const onSwiper = (swiper) => {
  // console.log(swiper)
};

const onSlideChange = () => {
  // console.log('slide change');
};


const activeRoomIdx = ref(null)

const clickOnRoom = (room, idx) => {
  activeRoomIdx.value = idx;
  emit('openRoom', room);
}

watch(() => props.room, (newVal, oldVal) => {
  if (!newVal) {
    activeRoomIdx.value = null
  }
})

</script>

<template>
  <div class="container">
    <h3>Rooms</h3>
    <div class="rooms-container">
      <span class="swiper-button prev"></span>
      <swiper
          :modules="swiperOptions.modules"
          :space-between="12"
          @swiper="onSwiper"
          @slideChange="onSlideChange"
          :navigation="swiperOptions.navigation"
          :breakpoints="swiperOptions.breakpoints"
      >
        <swiper-slide v-for="(room, idx) in roomStore.rooms">
          <div class="room-container" :class="{active: activeRoomIdx === idx}" @click="clickOnRoom(room, idx)">
            {{ room.name }}
          </div>
        </swiper-slide>
      </swiper>
      <span class="swiper-button next"></span>
    </div>
  </div>
</template>

Desks.vue

const props = defineProps({
  room: Object,
})

const {room} = toRefs(props)

defineEmits(['closeRoom'])


const employeeStore = useEmployeeStore()
const roomStore = useRoomStore()
const reserveData = reactive({
  employee: false,
  date: false,
  room: room.value.name,
  chosenDesk: false
})

const attrs = ref([])

const onDateChange = (range) => {
  console.log(range)
  console.log('hello')
}

const range = ref({start: new Date(new Date().getTime() -86400000), end: new Date(new Date().getTime() -86400000)})
const error = ref(false)

const makeDate = (date) => {
  let day = date.getUTCDate();
  let month = date.getUTCMonth() + 1;
  let year = date.getUTCFullYear();
  if (day < 10) {
    day = `0${day}`
  }
  if (month < 10) {
    month = `0${month}`
  }
  return `${day}-${month}-${year}`
}

const choseDesk = (newDesk) => {
  reserveData.chosenDesk = newDesk.name
  const desks = roomStore.rooms.find(room => room.name === props.room.name).desks
  const currDeskReserves = desks.find(desk => desk.name === newDesk.name).reserved
  attrs.value = currDeskReserves.map(cdr => {
    return {
      dot: 'red',
      popover: {
        label: cdr.person,
        visibility: 'hover',
        hideIndicator: false,
      },
      ...cdr
    }
  })
}

watch(() => range.value, (newVal, oldVal) => {
  reserveData.date = newVal
})

</script>

<template>
  <div class="container">
    <h3>{{ room.name }} Desks</h3>
    <div class="container-wrapper">

      <div class="desks-container">
        <div class="desk" :class="{active: reserveData.chosenDesk === desk.name}" @click="choseDesk(desk)"
             v-for="desk in room.desks">
          {{ desk.name }}
        </div>
      </div>

      <div class="inputs-container">
        <select name="employees" id="employees" @change="(e) => reserveData.employee = e.target.value">
          <option value="" selected disabled hidden>Für:</option>
          <option v-for="employee in employeeStore.employees" :key="employee" :value="employee">{{ employee }}</option>
        </select>
        <div class="from-to-container">
          <label for=""><span>Von:</span><input :value="makeDate(range.start)" type="text" id="from" disabled></label>
          <label for=""><span>Bis:</span><input :value="makeDate(range.end)" type="text" id="to" disabled></label>
        </div>
        <div class="buttons-container">
          <div v-if="error" class="error-message">Some days are reserved</div>
          <div class="buttons">
            <input class="small" type="button" value="Buchen" @click="roomStore.addNewReserve(reserveData)">
            <input v-if="error" class="small secondary" type="button" value="Abbrechen">
          </div>
        </div>
      </div>

      <div class="calendar-container">
        <VDatePicker
            @input="onDateChange"
            v-model.range="range"
            locale="de"
            trim-weeks
            :min-date="new Date()"
            color="indigo"
            :first-day-of-week="2"
            :attributes="attrs"
        />
      </div>
      <div class="close-button" @click="$emit('closeRoom')"></div>
    </div>
  </div>
</template>

答案1

得分: 3

When you initialize reserveData, you set the room property to the name of your current room ref:

const reserveData = reactive({
  room: room.value.name,
  ...
})

This is a value, not the reactive object. So the room prop is now a string, and it will not change when your room ref changes.

A simple way to fix it is to put the ref into reserveData and extract the room name from there, i.e. reserveData.room.name. Note that in Vue 3.3, you have to use toRef() with a function if you use it on props:

const room = toRef(() => props.room) // in Vue 3.3
const reserveData = reactive({
  room: room,
  ...
})

A better solution is probably to use a watcher that updates reserveData when the room changes:

watch(room, () => reserveData.room = room.value.name)
英文:

When you initialize reserveData, you set the room property to the name of your current room ref:

const reserveData = reactive({
  room: room.value.name,
  ...
})

This is a value, not the reactive object. So the room prop is now a string, and it will not change when your room ref changes.


A simple way to fix it is to put the ref into reserverData and extract the room name from there, i.e. reserveData.room.name. Note that in Vue 3.3, you have to use toRef() with a function if you use it on props:

const room = toRef(() => props.room) // in Vue 3.3
const reserveData = reactive({
  room: room,
  ...
})

A better solution is probably to use a watcher that updates reserveData when the room changes:

watch(room, () => reserveData.room = room.value.name)

答案2

得分: 1

你需要将reserveData对象定义为计算属性。

reserveData对象中的room属性不具有响应性,因为它仅在组件渲染时定义一次。如果你希望保持其中一个实际的值从一个prop中获取,那么reserveData对象必须定义为计算属性:

const reserveData = computed(() => ({
  employee: false,
  date: false,
  room: room.value.name,
  chosenDesk: false
}))

另外,你也可以不将reserveData定义为计算属性,而是创建一个watch函数来跟踪room prop的变化,并在reserveData对象中设置新的room值。

英文:

You have to make your reserveData object as a computed value.

The room property of reserveData object is not reactive because it's defined only once when component was rendered. If you want to keep there an actual value from a prop, the reserveData object must be computed

const reserveData = computed(() => ({
  employee: false,
  date: false,
  room: room.value.name,
  chosenDesk: false
}))

Also, instead of making reserveData computed, you can make a watch function that will track the prop room change and set a new value for room in the reserveData object.

huangapple
  • 本文由 发表于 2023年5月11日 15:45:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/76225209.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定