Using `this` to update useState in a function during build results in undefined error - Nuxt 3 & Strapi 4.12 - SFC (script setup)
P粉436410586
2023-08-03 16:40:32
<p><br /></p><h2><br /></h2>
<p><code>"@strapi/strapi": "4.12.0",</code></p>
<p><code>"nuxt": "^3.6.5",</code></p>
<p>In my development environment, I have a projects page with a category filter that shows either all projects or categories with corresponding projects. <br /><br />I am using useAsyncQuery to get items and categories from Strapi. <br /><br />In the filter, there is a drop-down menu with various categories. When these categories are clicked, the function switchCategory(filter.id) will be called. In this function, various states are updated using this.<br /><br />请查看以下脚本设置:</p><p><code></code></p>
<pre class="brush:php;toolbar:false;">
import { projectsQuery } from "~/queries/projects/archive";
import { filterQuery } from "~/queries/projects/filter";
const { data: filterRes } = await useAsyncQuery(filterQuery)
const { data: projectsRes } = await useAsyncQuery(projectsQuery)
var isLoading = useState('isLoading', () => false)
var filterActive = useState('filterActive', () => false)
var filterActiveText = useState('filterActiveText', () => 'Alle projecten')
const projectsUrl = useState('projectsUrl', () => '/projecten/')
const filters = useState('filters', () => filterRes.value.projectCategories.data)
const projects = useState('projects', () => projectsRes.value.projects.data)
var filteredProjects = useState('filteredProjects', () => projects)
function switchCategory(id) {
this.isLoading = true
this.filterActive = false;
document.getElementById("projects-container").scrollIntoView({
behavior: 'smooth',
});
setTimeout(() => {
if (id) {
this.filters.map((item) => {
if (id == item.id) {
this.filteredProjects = item.attributes.projects.data;
this.filterActiveText = item.attributes.Title;
}
});
} else {
this.filteredProjects = this.projects;
this.filterActiveText = 'Alle projecten';
}
this.isLoading = false;
}, 600)
}</pre>
<p>并且在模板元素中:</p>
<pre class="brush:php;toolbar:false;"><div class="flex flex-col gap-y-8 md:gap-y-24 lg:gap-y-40 transition-all duration-600 ease-in-out" :class="{
'blur-0': !isLoading,
'blur': isLoading,
}">
<div class="grid md:grid-cols-2 gap-8 md:gap-16 lg:gap-24 relative" v-for="(project, id) in filteredProjects" :key="id">
<Nuxt-Link class="absolute inset-0 w-full h-full z-10"
:to="projectsUrl project.attributes.Slug"></Nuxt-Link>
<div class="flex flex-col items-start justify-between">
<div class="sticky top-40 pb-16 lg:pb-20 prose-xl">
<h3 class="text-xl md:text-4xl lg:text-5xl font-bold">{{ project.attributes.Title }}</h3>
<p class="block">{{ project.attributes.Intro }}</p>
</div>
<Button class="flex mb-4" color="light" :to="projectsUrl project.attributes.Slug">
Project bekijken
</Button>
</div>
<div class="-order-1 md:order-1 relative">
<div class="aspect-video md:aspect-square lg:aspect-[12/18] relative">
<Media
:url="project.attributes.FeaturedImage.data.attributes.url"
:extension="project.attributes.FeaturedImage.data.attributes.ext"
/>
</div>
</div>
</div>
</div></pre>
<p>When I build the project, use nuxt build, and then use node .output/server/index.mjs to start a Node server, the project can load normally. But when I try to filter, while trying to update variables like isLoading and filterActive inside the switchCategory function, it throws the error: TypeError: Cannot set properties of undefined (setting 'isLoading'). <br /><br />Fix Attempt #1<br /><br />I thought, maybe after building, it doesn't understand this context (because when I try to console.log, this is also undefined). So I tried to refactor the code, like this: ;
<pre class="brush:php;toolbar:false;">const switchCategory = (id) => {
isLoading = true
filterActive = false;
document.getElementById("projects-container").scrollIntoView({
behavior: 'smooth',
});
setTimeout(() => {
if (id && filters) {
filters && filters.value.map((item) => {
if (id == item.id) {
filteredProjects = item.attributes.projects.data;
filterActiveText = item.attributes.Title;
}
});
} else {
filteredProjects = projects;
filterActiveText = 'Alle projecten';
}
isLoading = false;
}, 600)
}</pre>
<p>I changed the normal function to an arrow function and removed this. <br /><br />In setTimeout, I had to change filters.map to filters.value.map (not quite sure why the format changed). <br /><br />Now the code is running fine (no errors), but it is not updating the DOM of the item loop in the template. <br /><br />Fix Attempt #2<br /><br />After searching and debugging for quite some time, I found the content of defineExpose in the Vue documentation. <br /><br />It mentions that the script settings are "closed" by default. I (desperately) added this to my script settings, including all variables: </p><p><code></code><em></em></p> ;
<pre class="brush:php;toolbar:false;">defineExpose({
filterRes,
projectsRes,
pageRes,
isLoading,
filterActive,
filterActiveText,
projectsUrl,
filters,
projects,
filteredProjects,
})</pre>
<p>Unfortunately, as expected, it didn't magically work. <br /><br />Solution? <br />I'm probably looking at this the wrong way, but I can't think of any other solution.<br /><br />Am I missing something? </p><p><br /></p>
I'm not sure I can provide a complete working code snippet, but maybe I can guide you to a solution.
useState is only available when building or when the component is rendered. So changing the query parameters after rendering will not update it. Maybe re-rendering the component after a state change would help?