How to handle navigation when removing the current page's Vuex record?
Asked Answered
P

2

6

I have a ClientManagePage where I display client information and allow for the removal of the displayed client.

The vue-router route configuration for that page looks like this:

{
  path: '/client/:id/manage',
  name: 'client',
  component: ClientManagePage,
  props: ({ params }) => ({ id: params.id }),
}

The client entities are stored in a vuex store. ClientManagePage gets its client entity from the store using the id prop and displays various properties of the client and a "remove" button.

The remove button listener is (inside a mapActions):

async removeClientClicked(dispatch) {
  // Wait for the action to complete before navigating to the client list
  // because otherwise the ClientListPage might fetch the client list before
  // this client is actually deleted on the backend and display it again.
  await dispatch('removeClientAction', this.id);
  this.$router.push({ name: 'clientList' });
},

The vuex action that removes a client is:

async function removeClientAction({ commit }, id) {
  // Remove the client from the store first (optimistic removal)
  commit('removeClient', id);
  // Actually remove the client on the backend
  await api.remove('clients', id);
  // Moving "commit('removeClient', id);" here still produces the warning mentioned below
}

My problem is how to handle navigating to the other route when removing a client. The current code produces warnings in development mode such as:

[Vue warn]: Error in render: "TypeError: Cannot read property 'name' of undefined"

found in

---> <ClientManagePage> at src/pages/ClientManagePage.vue
       <Root>

This is of course caused by the reactivity system kicking in and trying to update the content of the page with the now-deleted vuex client entity. This happens before the removeClientAction is completed therefore the navigation to the ClientList page.

I've come up with some possible solutions to this, but they are not very appealing:

  • Have a v-if="client" at the top of the ClientManagePage that hides everything while the client does not exist in the store.
  • Use the computed property client in ClientManagePage to return a default "dummy" client that contains the required properties for the page. The page will still flash with "fake" content while the action is underway though.
  • Navigate to "clientList" right after (or even before) dispatching removeClientAction. This causes the clientList to display the removed client briefly while the action completes which is not good.

Are there other solutions to this seemingly common problem of navigating away when deleting the underlying vuex entity displayed on the current page?

Preceding answered 25/5, 2019 at 10:13 Comment(2)
How does your view / component look? Maybe use computed to set a default value if store doesn't have one, then your view won't blow up when the record gets deleted.Erythrism
@Erythrism There is a computed property for the client from which everything else is derived. Other parts of the component have interpolations like {{ client.name }} or {{ client.account.active }}that use the computed property as their root object. I'll add your suggestion to the list of possible solutions in the question.Preceding
P
0

I ended up doing a big v-if at the top of the ClientManagePage that hides everything while the client does not exist in the store. It's not pretty, but it works. An improvement could be to display a "please wait, operation in progress" in v-else.

Preceding answered 12/9, 2019 at 7:44 Comment(0)
E
0

One option is to externalize the deletion of the record. There are a number of ways to do that, but the simplest for me was to create a new route, /records/delete/:id, and place a route guard on that route that triggers the removal. Then redirect to the records list where you wanted to go in the first place. Something along the lines of:

import store from "wherever/your/store/is";

const routes = [{
    path: "/records/delete/:id",
    name: "deleteRecord",
    props: true,
    beforeEnter: (to, from, next) => {
        store.dispatch("DELETE_RECORD", to.params.id).then(() => console.log("record deleted!"));
        next({name: "some/path/you/wanted/to/go/to"});
    }
}, ...];
Erminia answered 18/5, 2022 at 18:52 Comment(1)
That sounds really promising. What happens if store.dispatch takes a long time to resolve? Does that stop all reactive updates in the displayed page, therefore avoiding the error? Or does it display an empty page?Preceding

© 2022 - 2024 — McMap. All rights reserved.