Switching between two identical components can be easily achieved with VueJS

Assume I have a file named foo.vue, which I import into the parent component as components called a and b, and they are displayed based on a variable called show.

When switching between components a and b in the parent without setting show = null, various child state errors occur.

To resolve this issue, I tried setting show to null before assigning it to the respective Vue component like this:

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

However, this approach did not work as expected. The component ended up retaining the state of the previous one, even for props that were not updated.

I managed to make it work by using a timeout:

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

Is there a more elegant solution available? The current method does not seem very clean to me.

It seems that what happens within the child component should not affect the switch triggered by the parent; it should start fresh and rebuild. However, in my case, this does not happen. Are there any factors causing this issue?

Does setting to null trigger a hard refresh somehow?

My child component is complex with AJAX calls to retrieve a list but nothing extraordinary.

Parent Component Code:

<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>

Child Component Code:

Fetching API data in mounted():

  mounted() {
    this.currentMode = this.mode
    if(this.resumeData && this.resumeData.slug){
       this.selectedSlug = this.resumeData.slug
    } else {
       this.resume(this.resumeData)
       this.getEvents()
    }
   }

Answer №1

This section serves little purpose:

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

The main advantage of JavaScript modules (including ES modules) is that they are only evaluated once upon initial import, meaning import( './events.vue') will point to the same component. This provides clarity on how a component is being utilized under one name.

When an update occurs with show, there will be one instance of the events component within the component hierarchy, avoiding remounting and simply receiving new props defined in <Events> and <AttendingEvents> respectively.

To differentiate between sibling instances of the same component, a key should be used:

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

While commonly used with v-for, this practice can also be applied to directives like v-if that impact the component hierarchy.

Answer №2

It seems like the issue you're facing stems from making an API call to modify data in the component within the mounted hook.

When you switch components without using a timeout, the child component is already rendered and active, but it changes props. By introducing a timeout and updating this.show = null before rendering, the component won't be displayed initially. Subsequently, when switching to the correct component within the timeout, the component will re-render which triggers the mounted hook.

To tackle this issue effectively, consider implementing a watcher within the child component that monitors changes for each instance of the component (e.g., through an ID). This watcher can then facilitate API calls based on those changes.

For example, include an 'id' prop and watch it as follows:

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
}

By setting immediate: true, the watcher will trigger upon mounting the component, eliminating the need to call it within the mounted hook again.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Accessing PHP output within Jquery

Even though I know PHP is a server-side script and JavaScript is client-side, I encountered an issue. I struggled to bypass browser security when making an AJAX request to another domain. Feeling lost, I decided to turn to PHP for help. The challenge I f ...

Capturing jQuery ajax requests in Angular

Within my Angular application, I am utilizing the integrated $.couch API from CouchDB. My goal is to intercept every ajax request, regardless of whether it originates from Angular's $http service or jQuery's $.ajax (which is utilized by $.couch). ...

When choosing the child option, it starts acting abnormally if the parent option is already selected in Angular

I am encountering an issue while trying to select the parent and its children in the select option. The concept is to have one select option for the parent and another for the child. I have parent objects and nested objects as children, which are subCatego ...

Loading Ajax content on a webpage

Recently, I've been impressed with the layout of Google Play Store on a web browser. I am eager to replicate that same smooth browsing experience on my own website. Specifically, I want to create a feature where selecting a category or item doesn&ap ...

Discovering the values within a separate JSON object

I have a collection of json objects that include information about different languages and user details. Languages User Details The user details contain a field for languages, which can have multiple values. Below is a sample json: $scope.languages = ...

Is there a way to display the inbox area upon completion of the document loading process?

I'm currently working on creating an inbox feature for my personal portfolio, mostly as a learning exercise rather than for any practical use. However, I've run into an issue where all emails are being displayed by default when the page loads wit ...

What is the best way for me to automatically square a number by simply clicking a button?

How do I create a functionality that multiplies a number by itself when a specific button is clicked? I want this operation to only occur when the equals button is pressed. I have been attempting to set this up for over an hour without success. My current ...

The image zoom function is malfunctioning when trying to adjust the image position

Having some trouble with a code for image zoom in/out. When I adjust the position of the image by applying margin left to either the image or the image container (#view), it affects the zoom functionality - causing the image to move to the left during zoom ...

Utilizing Jquery Autocomplete with multiple arrays for data sourcing

I am facing a challenge where I want to display both doctors and their connections in an autocomplete search using jQuery. Although I have successfully displayed the doctors, I'm struggling to categorize and display data from both arrays separately un ...

Alignment issue with Ul dropdown menus and shadow

After stumbling upon a fascinating menu design at this link, I decided to tweak it for center alignment by following the advice on this forum thread. Unfortunately, my efforts have hit a roadblock - the drop down menus aren't aligning as intended and ...

Incorporate a distinct identifier for every menu item within the Material UI TablePagination

Can a unique identifier be assigned to each menu item generated using the <TablePagination> component? I would like to add a specific ID (e.g., id="menu_item_0", id="menu_item_1" & id="menu_item_2") to the menu item ...

Issues with Vuejs3 Pagination Functionality Leading to Errors

Having trouble with implementing pagination? Make sure to limit to 10 pages using `_limit` with `params=ref`. I attempted to set up pagination from axios to params by defining the first page as 1, but unfortunately, the number is not being displayed. No er ...

Getting form field values with JQuery

I am currently facing an issue with a form field in HTML. Here is the code snippet: <form id="tasklist"> <input type="text" id="name" ...></input> ... additional form elements go here ... </form> Although I am trying to retrie ...

Looking for an alternative to document.querySelectorAll?

My issue involves using querySelectorAll('a') to select all buttons, but I only want to target two specific buttons labeled 'Know More'. How can I achieve this? Below is the code snippet in question: const buttons = document.query ...

HTML Buttons Remain Selected After Being Clicked

Currently working on a calculator app for a class project but I've hit a roadblock while setting up keyboard listeners. The keyboard functionality is fine, however, when it comes to clicking on buttons, specifically the non-keyboard functions, they re ...

Is it possible to prevent the late method from running during the execution of Promise.race()?

The following code snippet serves as a simple example. function pause(duration) { return new Promise(function (resolve) { setTimeout(resolve, duration); }).then((e) => { console.log(`Pause for ${duration}ms.`); return dur ...

The useEffect hook in React is signaling a missing dependency issue

Any tips on how to resolve warnings such as this one src\components\pages\badge\BadgeScreen.tsx Line 87:6: React Hook useEffect has a missing dependency: 'loadData'. Either include it or remove the dependency array react-hoo ...

Choosing read-only text fields using JQuery

I am working on an ASP.NET MVC project and I am looking to select all text boxes with the "readOnly" attribute on the current page. Once identified, I want to trigger a modal jQuery dialog on keypress for each of these disabled text boxes. Any suggestion ...

Error Encountered - Configuring Node.js Deployment on the Heroku Platform

"APPLICATION ERROR - Oops! Looks like something went wrong with the application and we couldn't load your page. Please give it another shot in a little while. If you are the app owner, make sure to review your logs for more information." Hey there - ...

Creating a dynamic input box with an add/remove button in each row using jQuery

Need help with a jQuery-based UI that allows users to dynamically add input boxes. The desired look is as follows: Default appearance: INPUT_BOX [ADD_BUTTON] [REMOVE_BUTTON] Clicking on the [Add_Button] should add another row like this, and so on: ...