Nuxt 3 useLazyFetch() blocks navigation - asynchronous

I supposed that the useLazyFetch composable from Nuxt 3 shouldn't block navigation, however, in my case, it does.
I don't know what I'm doing wrong...
<template>
<div v-if="!pending">
{{ data }}
</div>
<div v-else>
Loading
</div>
</template>
<script lang="ts" setup>
const { data, pending } = useLazyFetch('/api/test')
</script>
this is what the test api endpoint looks like:
export default defineEventHandler((event) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve('works')
}, 2000)
})
})

So... I figured it out.
If you don't want to have the initial request resolved on the server, you need to pass server: false to the options.
Example:
<script lang="ts" setup>
const { data, pending } = useLazyFetch('/api/test', {
server: false
})
</script>

Related

Why the context.slots in the Vue setup() function has an empty object?

Accordingly to this Issue it should work with the current version v3.2.x.
But it doesn't.
Here is the playground:
const { createApp } = Vue;
const myComponent = {
template: '#my-component',
setup(props, { slots }) {
console.log(slots)
}
}
const App = {
components: {
myComponent
}
}
const app = createApp(App)
app.mount('#app')
<div id="app">
<my-component>Default
<template #footer>Footer</template>
</my-component>
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<script type="text/x-template" id="my-component">
<div>
<slot></slot>
<hr/>
<slot name="footer"></slot>
</div>
</script>
The solution was provided by Duannx.
With console.log(slots) they are listed correctly.
{
"footer": (...n)=>{o._d&&Tr(-1);const r=hn(t);let s;try{s=e(...n)}finally{hn(r),o._d&&Tr(1)}return s},
"default": (...n)=>{o._d&&Tr(-1);const r=hn(t);let s;try{s=e(...n)}finally{hn(r),o._d&&Tr(1)}return s}
}
Explanation
JSON.stringify doesn't show the slots since they are functions.
Here is the explanation from the MDN Docs JSON.stringify():
undefined, Function, and Symbol values are not valid JSON values. If any such values are encountered during conversion, they are either omitted (when found in an object) or changed to null (when found in an array). JSON.stringify() can return undefined when passing in "pure" values like JSON.stringify(() => {}) or JSON.stringify(undefined).
Example
console.log("JSON.stringify(() => {}): " + JSON.stringify(() => {}));
console.log(JSON.stringify({ "func": function () {}, "lmbd": () => {} }))

Vue3 composition API and pinia | submit data then reload the data

I'm learning Vue 3 composition API and Pinia. I'm making a todo.
When I submit a todo data through Pinia, I can submit to the DB, but it won't re-render until reload the page.
Do I need to use 'watch' to watch the state todos:[] and execute fetchTodos()?
any good solution?
here both codes, hope someone can help me. Thank you in advance.
----- VUE -----
<script setup>
import { ref, onMounted } from 'vue'
import { storeToRefs } = from 'pinia'
import { useTodoStore } from '../store/todo'
const store = useTodoStore()
const { getTodos } = storeToRefs(store)
onMounted(() => {
store.fetchTodos()
})
const todo = ref('')
const initForm = () => {
todo.value = ''
}
// submit via Pinia
const onSubmitToPinia = () => {
const payload = {
todo: todo.value,
}
store.addTodoFromPinia(payload)
initForm()
store.fetchTodo()
}
</script>
<template>
<h4>TODO</h4>
<!-- form addTodo -->
<form class="row g-4">
<div class="col-auto">
<input
class="form-control"
v-model="newName"
type="text"
placeholder="todo">
</div>
<div>
<button
class="btn btn-primary"
type="button"
#click="onSubmitToPinia(payload)">
submit through pinia</button>
</div>
</form>
<!-- render data from pinia -->
<div class="todo"
v-for="getTodo in getTodoss.todo"
:key="getTodo.id">
<b class="ms-2">{{ getTodo.todo }}</b>
</div>
</template>
---- PINIA ----
import { defineStore } from 'pinia'
import axios from "axios"
export const useAboutStore = defineStore('todo',{
state: () => {
return {
todos: []
}
},
getters: {
getTodos(state) {
return state.todos
}
},
actions: {
async fetchTodos() {
try {
const data = await axios.get('http://localhost:5000/todo')
this.todos = data.data
}
catch (error) {
alert(error)
console.log(error)
}
},
addTodoFromPinia(payload) {
const path = 'http://localhost:5000/todo'
axios.post(path, payload)
}
},
})
You don't need to use storeToRefs to accomplish what you want nor do you need to watch the state of the store.
<template>
<div class="todo"
v-for="getTodo in store.todos"
:key="getTodo.id">
<b class="ms-2">{{ getTodo.todo }}</b>
</div>
</template>
If for any reason the vue complains that the array is empty put a v-if checking if the store.todos.length is != 0.
And also fix your typos.
If the problem persists show me your new code and I help you again.

Why my async fetch doesn't work client side?

I'm having an issue to get my component working client side with fetch (it's ok when I'm refreshing the page) in Nuxt.
The $fetchState.error doesn't load any error though. But my console.log in my mounted hook doesn't load any data.
I don't know what I'm doing wrong here. I'm using target: 'static'
<template>
<p v-if="$fetchState.pending">Fetching realisation...</p>
<p v-else-if="$fetchState.error">An error occurred :(</p>
<nuxt-link v-else :to="real.full_slug" class="real">
<div class="real__content">
<h2>{{ real.name }}</h2>
</div>
</nuxt-link>
</template>
<script>
export default {
props: {
realisation: {
type: Object,
required: true
}
},
data() {
return {
real: {}
}
},
async fetch() {
this.real = await this.$storyapi
.get(`cdn/stories/${this.realisation.work}`, {
version: 'published',
find_by: 'uuid'
})
.then((res) => {
return res.data.story
})
.catch((err) => console.log(err))
},
mounted() {
console.log(this.real)
},
fetchOnServer: true
}
</script>

vue 3 data() not workink setup() working but why?

What is the difference? Why does one work and the other why not? The latter is included in the documentation but does not work. Is there something wrong with the webpack? I use laravel-mix
This code snippet work:
<template>
<button #click="log">click me<button>
</template>
<script>
export default {
setup() {
const log = () => console.log('run');
return {
log
};
}
}
</script>
This code snippet didn't working:
<template>
<button #click="log">click me<button>
</template>
<script>
export default {
methods: {
log() {
console.log('run');
}
}
}
</script>
Both should not work, because you forgot to close your button and that should lead to a compiler error.
Anyways, if you fix the errors in your markup both should work.
Here you are using Vue's Options Api.
<template>
<button #click="log">click me</button>
</template>
<script>
export default {
methods: {
log() {
console.log("run");
},
},
};
</script>
Here you are using Vue's Composition Api
<template>
<button #click="log">click me</button>
</template>
<script>
export default {
setup() {
const log = () => console.log('run')
return {
log
}
}
};
</script>
Maybe there's a chance you also disabled the Options Api in your webpack.mix.js?

Vuex state change does not trigger reactivity on the page

I'm making a captcha component using vue. I try to fetch the captcha when the component created. When I do this in a async manner, the page will not be reactive, although the state has already been updated. But I when do it in sync manner, everything is fine. So, I'm wondering why async manner won't work?
This works
<template>
<section>
<div class="captcha-container">
<div v-if="captcha" v-html="captcha.data"></div>
</div>
</section>
</template>
<script>
import { mapState, mapActions } from "Vuex";
export default {
created: function() {
this.$store.commit('setCaptcha', {id: 'xx', data:'Hi'});
},
computed: {
...mapState(["captcha"])
},
};
</script>
This does not work
<template>
<section>
<div class="captcha-container">
<div v-if="captcha" v-html="captcha.data"></div>
</div>
</section>
</template>
<script>
import { mapState, mapActions } from "Vuex";
export default {
created: function() {
setTimeout(() => {
this.$store.commit('setCaptcha', {id: 'xx', data:'Hi'});
}, 1000);
},
computed: {
...mapState(["captcha"])
},
};
</script>

Resources