VueJS: лучший способ переключения между двумя экземплярами одного и того же компонента

avatar
Sam
8 августа 2021 в 16:14
188
2
0

Допустим, у меня есть foo.vue, который я импортирую в родительский элемент как компоненты с именами a и b и отображаемые в соответствии с переменной show

Теперь от родителя, если я переключаюсь между компонентами a и b без установки show = null, я получаю всевозможные ошибки дочернего состояния.

Поэтому я установил show на null, прежде чем установить его для соответствующего компонента vue, например

this.show = null
this.show = a // or b

но это не работает. компонент заканчивается состоянием предыдущего. Это подтверждается даже для реквизитов, которые не обновляются.

Я заработал, используя timeout

toggle(show){
   this.show = null
   let that = this
   setTimeout(() => {
      that.show = show
   }, 200)
 }

Есть ли лучший способ? Для меня это не элегантно

Я понимаю, что то, что происходит внутри ребенка, не имеет значения, если родители говорят ему переключиться, он должен начать с чистого листа и перестроиться? Но в моем случае это не так. Есть ли что-то, что может вызвать мою проблему?

значение null каким-то образом принудительно обновляет ?

Мой дочерний компонент сложен из-за вызова ajax для получения списка, но ничего необычного.

родительский код

<template>
<div id="userAreaEventWrapper">
    <div class="userarea-submenu">
        <div v-on:click="toggle(comps.eventForm)" class='button-base-out'>
            <div :class="isSelected(comps.eventForm)">
                New Event
            </div>
        </div>
        <div v-on:click="toggle(comps.events)" class='button-base-out'>
            <div :class="isSelected(comps.events)">
                My Events
            </div>
        </div>
      <div v-on:click="toggle(comps.eventsAttending)" class='button-base-out'>
        <div :class="isSelected(comps.eventsAttending)">
          Attending
        </div>
      </div>
    </div>
    <EventForm v-if="show === comps.eventForm" @created="toggle(comps.events)" :mode="'create'"></EventForm>
    <Events ref="events" :mode="currentMode" v-if="show === comps.events" @noResults="toggle(comps.eventForm)" :attending="false"></Events>
    <AttendingEvents ref="attending" :mode="'home'" v-if="show === comps.eventsAttending" :attending="true"></AttendingEvents>
</div>
</template>

<script>
const EventForm = () => import( './event-form.vue')
const Events = () => import( './events.vue')
const AttendingEvents = () => import( './events.vue')

export default {
    name: "UserEventComponent",
    components:{
        EventForm,
        Events,
        AttendingEvents
    },
    data(){
        return{
            comps:{
              eventForm:1,
              events:2,
              eventsAttending:3
            },
            show: 2,
            currentMode:'user',
        }
    },
    methods: {
        toggle(show){
          this.show = null
          let that = this
          setTimeout(() => {
            that.show = show
          }, 200)
        },
        isSelected(n){
          return n === this.show ? 'button-base-in button-base-in-hover' : 'button-base-in'
        },

    },
}
 </script>

ребенок

выбрать API на mounted()

  mounted() {
    this.currentMode = this.mode
    if(this.resumeData && this.resumeData.slug){
       this.selectedSlug = this.resumeData.slug
    } else {
       this.resume(this.resumeData)
       this.getEvents()
    }
   }
Источник
Estus Flask
8 августа 2021 в 17:09
0

Пожалуйста, предоставьте coderhelper.com/help/mcve для вашей проблемы. Непонятно, как именно это выглядит. но это не работает, потому что JS асинхронный правильно - это асинхронный или синхронный, почему это не работает, зависит от вашего случая.

Sam
8 августа 2021 в 17:13
0

Да, хорошо, это не очень хорошее заявление, я знаю, что js может быть асинхронным или синхронным. Но это на самом деле не связано с JS, я думаю, а скорее с vue? Я нигде не вижу в сети, что вы должны использовать промисы или асинхронность или использовать тайм-аут для разделения состояний.

Estus Flask
8 августа 2021 в 17:41
0

Пожалуйста, опубликуйте код, а не описывайте его, это требуется правилами SO, чтобы избежать двусмысленности. Вы можете понять, почему это важно, пользователь уже опубликовал ответ, который не решил проблему, потому что он не соответствует фактическому коду, который у вас есть.

Sam
8 августа 2021 в 17:42
0

Вы хотите, чтобы я тоже разместил ребенка?

Roamer-1888
8 августа 2021 в 17:42
0

Это помогает? coderhelper.com/q/41086548/3478010

Sam
8 августа 2021 в 17:44
0

@ Roamer-1888 кажется, что это соответствует симптомам, но я пока не очень хорошо это понимаю. Вы могли бы быть на нем.

Estus Flask
8 августа 2021 в 17:44
0

Пожалуйста, предоставьте coderhelper.com/help/mcve, по ссылке описано, что именно нужно, см. также coderhelper.com/help/how-to-ask. Если это означает, что вопрос должен содержать полный код для родительского и дочернего компонентов, то все. В настоящее время вопрос имеет смысл только для вас, потому что вы видели свой собственный код. Другие пользователи не знали и могут только догадываться. Насколько я понимаю, вам может понадобиться key для разных экземпляров одного и того же компонента, но неясно, так ли это.

Sam
8 августа 2021 в 18:02
1

@Roamer-1888 Roamer-1888 да, это было так, из поста, на который вы ссылаетесь, мне просто нужно было добавить :key="someUniqueValue" к каждому дочернему компоненту, и больше нет необходимости в тайм-ауте !! благодарю вас. Пожалуйста, напишите ответ, и я приму его

Roamer-1888
9 августа 2021 в 01:34
0

Сэм, я рад, что ты принял ответ Эстуса Фласка.

Ответы (2)

avatar
Estus Flask
8 августа 2021 в 18:11
0

Эта часть не служит хорошей цели:

const Events = () => import( './events.vue')
const AttendingEvents = () => import( './events.vue')

Основная функция модулей JavaScript (включая модули ES) оценивается только один раз при первом импорте, import( './events.vue') разрешается в тот же компонент. Это дает лучшее представление о том, что происходит, когда компонент используется под одним именем.

Что происходит при обновлении show, так это то, что есть 1 экземпляр компонента событий в том же месте в иерархии компонентов, поэтому он не монтируется повторно, а просто получает набор новых реквизитов, указанных в <Events> и <AttendingEvents> соответственно. .

Экземпляры одного и того же компонента должны различаться по key:

<Events ref="events" key="events" ... ></Events>
<Events ref="attending" key="attending" ... ></Events>

Она чаще всего используется с v-for, но также применима ко всем директивам, влияющим на иерархию компонентов - v-if и т. д.

avatar
Noy Gafni
8 августа 2021 в 18:04
0

Я думаю, ваша проблема в том, что вы вызываете API для изменения данных в компоненте из хука mounted.

если вы "переключаете" компоненты без timeout, то происходит то, что дочерний компонент уже визуализируется и активен, но он меняет свойства. когда вы вызываете timeout, вы меняете this.show = null перед тем, что компонент не будет отображаться, а затем внутри timeout вы меняете его на правильный компонент, и компонент снова визуализируется, что запускает хук mounted.

Лучшее решение этой проблемы — использовать watcher внутри дочернего компонента, который будет меняться для каждого экземпляра компонента (какой-то идентификатор). и вызовите API из наблюдателя.

например, добавьте реквизит id и посмотрите его:

props: {
  id: {
    type: Number,
    default: 0
  }
},
watchers: {
  id() {
     this.currentMode = this.mode
    if(this.resumeData && this.resumeData.slug){
       this.selectedSlug = this.resumeData.slug
    } else {
       this.resume(this.resumeData)
       this.getEvents()
    }
  },
  immediate: true
}

immediate: true также запускает наблюдатель при монтировании компонента, поэтому вам не нужно снова вызывать его внутри хука mounted.