vue3 - Update state in onBeforeRouteUpdate() - vuejs3

I'm a newbie and working with translation software, thanks for the guidance.
I have a routing component that updates count in onBeforeRouteUpdate() every time I navigate, it works fine, but the onUpdated() hook is called twice, I don't understand why, is there any way to avoid it
code:
<template>
<div>{{count}}</div>
</template>
<script setup>
import { ref, onUpdated } from 'vue'
import { onBeforeRouteUpdate } from 'vue-router'
const count = ref(0)
onUpdated(() => console.log('updated'))
onBeforeRouteUpdate((to, from) => {
count.value += 1
})
</script>
output twice in the console
[1]: https://i.stack.imgur.com/bfCEx.png

Related

vue & vitest, data-v-[random-number] attribute being added to html

Introduction: I am working with vite and vitest, I am doing snapshot tests for components that are 100% template and do not contain any logic
Problem: data-v-[random-number] It is being added to the root element of each component and snapshots are always different
What i want: Understand why im getting this data-v-[random-number] and if possible, a way to avoid this problem
Short example code:
BaseText.vue:
<script setup lang="ts"></script>
<template>
<span><slot></slot></span>
</template>
BaseText.spec.ts:
import { describe, it, expect } from "vitest";
import { shallowMount } from "#vue/test-utils";
import BaseText from "./BaseText.vue";
describe(name, () => {
const wrapper = shallowMount(BaseText);
it("MatchSnapshot", () => {
expect(wrapper.html()).toMatchSnapshot();
});
});
Error when runing tests:
- Expected ""<span data-v-25e5131c=""></span>""
+ Received ""<span data-v-b3462088=""></span>""

Next js and Next Auth overlapping react declarations

I am running Next js and Next Auth in multiple project, and all of a sudden all of them decided to crash with the same error.
Module parse failed: Identifier '_react' has already been declared (14:6)
File was processed with these loaders:
* ./node_modules/next/dist/build/webpack/loaders/next-swc-loader.js
You may need an additional loader to handle the result of these loaders.
| const _material = require("#mui/material");
| const _xDataGrid = require("#mui/x-data-grid");
> const _react = require("next-auth/react");
| const _reportTable = /*#__PURE__*/
a simple example that crashes looks like this...
As you can see from the example below. I am not importing react twice.
import React from "react";
import { Box } from "#mui/material";
import { DataGrid, GridColDef, GridRowsProp } from "#mui/x-data-grid";
import { getSession } from "next-auth/react";
import ReportTable from "../src/components/ReportTable";
export default function Home() {
const findSession = () => {
const session = getSession();
console.log(session);
return session;
};
return (
<Box>
<ReportTable title="Price Books">
<DataGrid
sx={{ border: "0" }}
rows={rows}
columns={columns}
headerHeight={40}
/>
</ReportTable>
</Box>
);
}
If I remove the getSession import at the top everything runs fine. The other developers on my team can run these project just fine, so I believe it's an environmental issue on my side.
Has anyone else run into this issue?
I have built the project and it works fine. The errors only occur in my dev environment.
I also cloned the repo on my personal machine and it worked fine there as well.
The problem was with a new plugin that came out today, "Code Ninja". If you are facing this issue, disable that VSCode extension.

Vue - using props on custom elements fails using createApp and mount?

We would like to pass props to custom elements that uses createApp
// index.html
<div id="my-root">
<my-element prop1="abc"></my-element>
</div>
// my-element.vue
<script lang="ts" setup>
const props = defineProps<{ prop1: number }>();
</script>
<template>
{{props.prop1}}
</template>
This works fine, but as our custome element get bigger we would like to register components and use e.g pinia and other tools. Do use those we need to add createApp and mount it. But then prop1 is always undefined
// main.ts
import ...<lots of imports>
import AppCe from "./AppWebComponent.ce.vue";
import { createPinia } from "pinia";
// Adding code below is causing prop1 to be undefined - if we skip this part, prop1 works fine
const pinia = createPinia();
const app = createApp(App);
app.use(pinia).use(ConfirmDialog);
app.component(...<lots of components>);
app.mount("#my-root");
const ceApp = defineCustomElement(AppCe);
customElements.define("my-element", ceApp);
update:
Here's a sample without: https://stackblitz.com/edit/vue3-script-setup-with-vite-56rizn?file=src/my-element/my-element-main.js
And here's a sample with the createApp: https://stackblitz.com/edit/vue3-script-setup-with-vite-gtkbaq?file=index.html
Any idea on how we could solve this?
We have a fallback, that is to do a getElementById and read the attribute value in the mounted callback - but that is not an optimal solution.
Thanks for any ideas!
update2:
Here's an attempt using #duannex suggestion. We're getting closer, the app is availible, components registered, but still no sigar. : https://stackblitz.com/edit/vue3-script-setup-with-vite-ofwcjt?file=src/my-element/defineCustomElementWrapped.js
Based on update2 with the wrapped defineCustomElement; Just pass the props to the render function:
render() {
return h(component, this.$props)
},
https://stackblitz.com/edit/vue3-script-setup-with-vite-vfdnvg?file=src/my-element/defineCustomElementWrapped.js

How to get current route via useRoute outside of router-view component

How can I get the current route using useRoute for a component that's outside of the <router-view />? Is this possible?
Breadcrumbs.vue
<script setup>
import {useRoute} from 'vue-router'
const route = useRoute()
console.log(route.name) // undefined
</script>
App.vue
<template>
<Breadcrumbs />
<router-view />
</template>
The alternative is that I have to put <Breadcrumbs /> at the top of every single view component, and I was hoping to avoid that and instead just include it once in my App.vue
route.name is undefined in your example because that's the initial value when the component is rendered before the route has resolved.
For the component to reactively update based on the current route, use a watcher (e.g., watch or watchEffect):
import { watchEffect } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
watchEffect(() => {
console.log(route.name)
})
demo
In your main file, try to mount app like this
router.isReady().then(() => {
app.mount('#app');
});
then useRoute() should be ready in your component

Cannont mount twice the same Vue 3 component

Im' mounting a Vue 3 component as a loading animation during a frame loading with :
createApp(App).mount(e);
When the frame is loaded, the component is erased in the html by the frame content (but not by Vue I guess). This behaviour is managed by an external system of Vue.
When I'm trying to reload the component with the same command, the component is not displayed.
If I only mount again the component, I have the following warning in the console and the component is not displayed :
"[Vue warn]: App has already been mounted.
If you want to remount the same app, move your app creation logic into a factory function and create fresh app instances for each mount - e.g. `const createMyApp = () => createApp(App)`".
I've found also a way to replicate the issue with :
const app = createApp(App)
app.mount(el);
el.innerHtml = '';
app.mount(el);
I also try to unmount the component with no more success :
const app = createApp(App);
app.mount(el);
app.unmount();
app.mount(el);
So what is the correct way to display again the same vue component when it is erased externally ?
To mount/unmount 2 instances of Vue, it is necessary to use render method.
So, for exemple below, this solution works :
var app = createApp({ render: () => h(App) })
app.mount(el);
el.innerHtml = '';
app = createApp({ render: () => h(App) })
app.mount(el);
Don't forget to import h() function with :
import { createApp, h } from 'vue';
An far more better option is to use Vue custom elements.
First, create a classical SFC with ce.vue extension :
//test.ce.vue
<template>
<div class="text-primary">Test</div>
</template>
<script>
export default {
name: 'test',
};
</script>
<style>
.text-primary {
color: red;
}
</style>
And then in the main script :
//app.js
import Test from 'test.ce.vue';
const testElement = defineCustomElement(Test);
customElements.define('test-element', testElement);
document.body.appendChild(document.createElement('test-element'));
The component will be rendered as soon as is detected in the document:
<test-component>
#shadow-root (open)
<style>
.text-primary {
color: red;
}
</style>
<div class="text-primary">Test</div>
</test-component>

Resources