0

Objective

Render components based on a dynamically changing reference (ref). User can perform a "search" feature that returns data and updates the reference. The updated reference should then in turn update the components which are rendered using v-for.

My Setup

I have an onMounted() lifecycle hook that makes an axios request and returns all the data into a reference.

onMounted(async () => {
    const response = await axios.get('/api/subject/')
    allSubjects.value = await response.data.data;
})

The reference:

const allSubjects = ref(null)

The template:

<OneSubject
    v-for="subject in allSubjects" 
    :key="subject.id"
    :subject="subject" 
/>

Everything works fine so far...

Problem

When I make another request for my "search" feature, the axios request works fine and I am able to get a response with data (a new array of objects).

The problem occurs when trying to update my reference with this data like so:

async function search(searchInput) {
    const response = await axios.get(`/api/subject/search/${searchInput}`)
    console.log(response) // <-- RESPONSE HAS DATA
    allSubjects.value = await response.data.data; // <-- CAUSES ERROR
}

The error that is thrown comes from the component that is rendered in the v-for:

enter image description here

I can verify that the reference was successfully updated with new data, but the problem seems to arise when rendering the component in the v-for?

Update

Here is the component that it is trying to render in the v-for.

<template>
    <div class="subject-wrapper" v-bind:style="{ background: `rgba(0, 0, 0, .3) url('${imgSrc}') center / cover` }">
        <div class="darken-bg"></div>
        <div class="subject-name">{{ subject.name }}</div>
    </div>
</template>

<script setup>

    import { onMounted, computed } from 'vue'

    const props = defineProps({ subject: Object }) 
    const imgSrc = computed(() => require(`@/assets/images/subject/${props.subject.image}`))

</script>

I updated my component as follows:

<template>
    <div class="subject-wrapper" v-bind:style="{ background: `rgba(0, 0, 0, .3) url('${imgSrc}') center / cover` }">
        <div class="darken-bg"></div>
        <div class="subject-name">{{ subject.name }}</div>
    </div>
</template>

<script setup>
    import { reactive, computed, onUpdated } from 'vue'
    
    const props = defineProps({ subject: Object })
    const subject = reactive(props.subject)
    const imgSrc = computed(() => require(`@/assets/images/subject/${subject.image}`))

    onUpdated(() => {
        console.log('component subject prop:', props.subject)
        console.log('subject reactive:', subject)
    })

</script>

Afterwards, the search executes without the error, but it renders the wrong component. I've console logged the values:

enter image description here

Solution

I found out that the error was actually coming from my server response. As you can see in the image above, the prop only has 2 values. When I am rendering the component, I require an image property that is non existent, thus throwing the error.

I updated my endpoint to return all the properties in the document and it now works.

Thanks yoduh for the suggestions that helped me get to the bottom if this!

4
  • 1
    you don't need to await response.data.data since there's nothing asychronous there after already awaiting response. besides that, I don't see anything else wrong with the code. The error might originate from the data returned by the search function but the error is not occurring in the search function. As the trace seems to say it's somewhere inside the child component. Can you show that code instead? Maybe console log around where the error line is and see if there's any invalid data being used or passed around. Commented Feb 3, 2023 at 16:44
  • Okay, getting closer. I am able to render it but the search function still doesn't return properly. I'll update the question with the behavior. Commented Feb 4, 2023 at 3:29
  • 1
    computed properties are for deriving a value based on other values. think of them as reactive functions. they perform some calculation or other operation on some given data and return a value. you can't directly mutate a computed property, but you can change the values of the data they perform their calculations on. computed properties are reactive in that they will automatically rerun themselves when they detect a value used in its calculation has changed, causing it to recalculate/rederive a new return value without you having to manually call it. Commented Feb 4, 2023 at 3:31
  • Thanks that makes more sense. I was able to solve the computed property issue, but now it seems like my reactive variable "subject" is not changing with the prop? I've updated the question to show the component code. Commented Feb 4, 2023 at 3:50

1 Answer 1

1

I can't explain the console.logs at the bottom of your question, but based on the child component code I believe there are two changes needed to fix the overall issue.

  1. require shouldn't ever be used in code when using Vite since require is ESM only. The Vite documentation describes a better way to import dynamic assets using new URL with import.meta.url. One caveat is that you can't use @ alias when constructing the URL due to Rollup limitations (Vite's bundler). Your imgSrc then should look like this:
const imgSrc = computed(
  () => new URL(`../assets/images/subject/${subject.value.image}`, import.meta.url).href
);

(actual relative path might be different on your local machine)

  1. Change from using reactive to toRef when creating subject. Since props is reactive and you want to pull out the individual (non-reactive) property, you would use toRef for the job specifically because it keeps subject and props.subject synced together.
const subject = toRef(props, 'subject');

I don't have your exact same setup but when I tested it locally with some mock data/local image files instead of an API it worked for me. If you still have problems let me know.

Sign up to request clarification or add additional context in comments.

4 Comments

I am just using Vue and don't have Vite. Maybe I should look into Vite?
I guess I was mistaken based on your use of Composition API but are not using Vue 3? Or maybe Vue 2.7 with webpack? I'll probably delete this answer then since it doesn't apply but I'll wait for your response
I'm using Vue 3 Composition API without webpack, not with Vite, but maybe I should consider bundling them together next time? I looked into it and it looks pretty sick. And I just figured out my mistake. It was actually in the API server side. I was calling and endpoint that only returned 2 of the values. So when I was passing the props successfully, the prop just didn't have the image value, which caused the error.
Also appreciate the suggestions, was very helpful having your input to help me solve this thing

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.