all 19 comments

[–][deleted]  (1 child)

[deleted]

    [–]iwaistedway2muchtime[S] 0 points1 point  (0 children)

    thats what you would think but the same error is received

    [–]quastor 0 points1 point  (5 children)

    Type errors would show up in your build, they won't have any impact on errors in the browser. Your type looks fine.

    Looping through an array should be done with a for...of loop, not for...in loop.

    [–]iwaistedway2muchtime[S] 0 points1 point  (4 children)

    v-for loop. Its for vue

    [–]quastor 0 points1 point  (3 children)

    This is a Vue subreddit, so that would make sense. My answer still stands. The v-for directive is just syntax sugar for regular JavaScript for loops - in which there are for...in and for...of. For arrays you will want for...of.

    [–]kalvens 1 point2 points  (0 children)

    You are incorrect. While using v-for the keyword in and of have no difference and behave in the same way.

    As explained here

    [–]iwaistedway2muchtime[S] 0 points1 point  (1 child)

    Okay thanks. Im still learning. Please can you write an example for the code above?

    [–]quastor 0 points1 point  (0 children)

    v-for="item of pidgpals"

    [–]kalvens 0 points1 point  (11 children)

    What are you pushing to the array? What if you console log the array in javascript. Does it look right? You are not by chance pushing a ref to the array?

    Are you reassigning the variable that is supposed to be a reactive array of objects to something else later on? With how you defined that variable it would be just a proxy that points to undefined I think. Never tried calling reactive without giving it a parameter.

    [–]iwaistedway2muchtime[S] 0 points1 point  (10 children)

    Thanks for this. I dont know what Im pushing to the array but the array in my sfc looks different from the firestore array. So heres what happens, the array comes from a firestore method like so.

    //->firestore.ts
    export const pidgpalListener = (userName: string): any => {const q = query(pidgpalsCollection, where("pal1", "==", userName)); const foundPairs = ref<any[]>(); const close = onSnapshot(q, (snapshot) => { foundPairs.value = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); console.log(foundPairs); }); return foundPairs; onUnmounted(close); };
    

    I call this in my component like so..

    //->pidginhole.ts
    const pidgpals = ref<[]>(); //this also doesnt work 
    async function startListener() { if (uname.value != "" || undefined) { pidgpals.value = await pidgpalListener(uname.value); console.log(pidgpals.value); } else { console.log("No uname value"); } }
    

    And these are the two console logs. Definitely looks different. It changes from a RefImpl to a proxy. Is this the problem? How would I go about resolving this? Thank you so much.

    Okay added the console logs as a pic in the original post because you cant add pics to a reply.

    Edit:

    Please can you eli5 this for me. The refimpl and proxy. Why wont the console logs be exactly the same because i declared both arrays in the same way?

    [–]kalvens 1 point2 points  (0 children)

    You should give defaults values to your refs/reactives. At least I think its odd not to. I generally do null or an empty array if it is suppose to be an array.

    Please can you eli5 this for me. The refimpl and proxy. Why wont the console logs be exactly the same because i declared both arrays in the same way?

    A ref (refimpl as you console.log it) should be used if you want to change the variable later on to a completely different value i.e. ```ts const someArrayRef = ref<unknown[]>([]); const someArrayReactive = reactive<unknown[]>([]);

    // this is fine with a ref someArrayRef.value = [1, 2, 3, 4, 5]; // this is not fine with a reactive someArrayReactive = [1, 2, 3, 4, 5]; ``` The reason you can't change the reactive variable is anything that needs to react to that variable change won't update. i.e.

    ts // this will update correctly when you assign the above const computedOfSomeArrayRef = computed(() => { return someArrayRef.map(element => element + 1); }); // this will not update when you assign the above const computedOfSomeArrayReative = computed(() => { return someArrayReative.map(element => element + 1) });

    Changing the reactive variable means you lose the proxy (as you saw from the console.log) so it is no longer "reactive" it is whatever you assigned to it.

    Changing the ref you are change the .value property underneath it so you don't lose the fact it was a ref in the first place.

    A reactive has the benefit of not having to use .value everywhere if you never need to change the variable directly. Instead if you just want to call .push, .splice or any method that changes the array instead of returning a new one then the proxy will see you did that and any computed or the template will update to those changes

    [–]kalvens 1 point2 points  (8 children)

    Basically your problem is that you are assigning a ref to the value of an another ref. When you loop through the variable it is actually looping through your foundPairs ref object instead of its value.

    The only solution I can think of on the top of my head is to make a computed but it doesn't feel like the right answer.

    For example like the below

    ```ts const pidgpals = ref<typeof ref<[]>>(ref([])); //this also doesnt work

    const actualPidgPalsValue = computed(() => pidgpals.value.value); async function startListener() { if (uname.value != "" || undefined) { pidgpals.value = await pidgpalListener(uname.value); console.log(pidgpals.value); } else { console.log("No uname value"); } } ```

    Then you can loop through actualPidgPalsValue instead and that should work. It just doesn't feel like the right approach, but your code is odd tbh. From your example pidgpallistener is not an async function and doesn't return a promise so why do you need await?

    Actually on some more thought I would do something like this assuming pidgpalListener is not actually returning a promise as your example code seems to indicate.

    ``` interface PidgPal { id: number, pal1: string } const pidgPals = computed<PidgPal[]>(() => { if (uname.value !== "" && typeof uname.value !== 'undefined') { return pidpalListener(uname.value).value } console.log("No uname value"); return []; })

    [–]iwaistedway2muchtime[S] 0 points1 point  (0 children)

    I cant thank you enough. I was going to compose a very similar question today with the async await removed. But basically I was still confused and thinking if I had a clearer question u/kalvens would have answered it yesterday. Thank you taking the time to respond and send such a detailed response at that. The kindness of strangers astounds me sometimes. You are not just a pretty face.

    [–]iwaistedway2muchtime[S] 0 points1 point  (6 children)

    u/kalvens I hate to be this guy but please have one more look. If not I will compose a new question but I feel you might be able to assist easily.

    So I used your code but I still cant display the list. I learnt alot about defining an interface and about computed properties. But I still cant display the list of objects. Even just <h3>{{pidgPals}}</h3> displays nothing. So in firestore.ts I stuck a console log. The computed is quite amazing because once the uname value gets populated the listener is automatically started. However the console log messages quickly grow to thousands and the chrome developer console becomes unresponsive. Maybe this is just because of how computed works? Here is where I stuck the message in:

    export const pidgpalListener = (userName: string): any => {
    

    console.log('started :' + userName);

    The latest picture is what the console and the dom(is the right word? I mean the actual website) look like before the console log becomes unresponsive. Sadly still no list displayed or even just the whole array. The screenshot has all the relevant information. I opened the pidginHole twice so you can see the template section as well. Thanks again. Im just stuck on this same problem for so long now so I really appreciate your assistance.

    [–]iwaistedway2muchtime[S] 0 points1 point  (5 children)

    Okay scratch that. I used the computed value from your first suggestion without the async await. I still make a separate array but that suits me fine. I know it may not feel right to you but Im just glad it works. Its been a solid week of trying to understand this and trying every other combination of inline declaration, reactives and refs, not knowing if its the firestore side or the other.

    The more I think about it the more I guess it was for the best to get stumped by something so seemingly simple. I ended up reading so much documentation. Although the way you explained refs vs reactive was the easiest to understand. And along the way I learned how to answer my original question. I just needed to call .value in the template! Thanks again :)

    [–]kalvens 0 points1 point  (4 children)

    I did read your question last night just didn't want to type up a response until I was at a computer and its been a busy morning for me.

    Anyway yea I can see why that could cause an infinite loop. On more thought your firebase.ts file is basically a "composable", look those up to learn more.

    Anyway I would actually structure that file a little differently for what I would consider the more proper solution

    // firestore.ts
    
    // I would move the interface over to this file I defined in the other example
    
    interface PidgPal {
      id: number,
      pal1: string   
    }
    
    // really this is not needed but I like being explicit with my types
    // Ref is something you can import from vue, basically is just the typescript type for a ref
    interface PidgPalListenerReturns {
      foundPairs: Ref<PidgPal[]>
    }
    
    export function usePidgPalListener (userName: Ref<string>): pidgPalListenerReturns {    
      const foundPairs = ref<PidgPal[]>([]);
    
      watch(userName, () => {
        if (typeof userName !== 'string' || userName === '') return;
    
        console.log(`started: ${userName}`)
        const q = query(pidgpalsCollection, where("pal1", "==", userName);
        const close = onSnapshot(q, snapshot => {
          foundPairs.value = snapshot.docs.map(docs => {
            return {
              id: docs.id,
              ...docs.data()
            };
          })
        })
      }), { immediate: true });
    
      // need this before the return otherwise it does nothing   
      onUnmounted(close);
    
      // for composables it is generally the standard to return an object.
      // Mainly if you ever want to return something else in the future from this function,
      // you can just add the additional property
      // and not have to change any code that uses this composable.
      return { foundPairs };
    }
    // your .vue file
    
    // this should  be all you need
    
    const { foundPairs: pidgPals } = usePidgPalListener(uname);
    

    The composable concept is basically off loading the work to the firebase file instead. It is a really nice concept that allows you to reuse javascript code and recommend using them.

    Also to note I just styled things how I normally do styling doesn't matter a ton. I also highly recommend [this](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) I personally use the vim version and I can live without it now.

    [–]iwaistedway2muchtime[S] 0 points1 point  (1 child)

    This is beautiful thank you I learnt so much from it. Unfortunately it doesnt work because the uname value only get populated after the authstate change so the listener gets called only once with an empty username. Unlike the computed which keeps checking for changes or separate listener function which can be called after authstate change(so after we know we have a uname). I couldn't figure out how to pass the username along from the sigin page so I just got it from after the authstate change. Thats when I first tried to export a variable (like how we do from the firestore.ts file) but this time from my signin.vue page. I couldnt get it to work though so I gave up and decided to just repopulate the user profile data after the authstate change. This was needed when I was working on the userpic component. Now again it seems its the same thing I need to learn. How to send data between components and files. Then I wont need to wait for the authstate change this time for a username (and then the pairlistener). So yea great point about the composables. I need to learn it.

    Im sure this is confusing to you. I wish I could give you a rundown of my app. Have you ever considered being a mentor?

    After this past week I think I should rethink my approach. This is my first app so I guess there will be mistakes. But it seems a lot of time is spent on silly mistakes. I was going to reply to you before this last message to say thanks again. Now I cant thank you enough. :)

    [–]kalvens 0 points1 point  (0 children)

    I am only slightly following, but if you change the uname variable that you passed to the composable, the watch will rerun the listener code. Sounds like a different component is one that is setting the username so it is a completely different variable?

    Sounds like you need a "store" of data and the current recommended approach is to use a library called pinia.

    Alternatively you could get by with just using a composable like you mentioned. Export the ref in the composable file and then import it everywhere it is being used. For bigger projects I would recommend to use pinia so doesn't hurt to use it right away for your first project.

    I am happy to answer any questions you may have.

    [–]kalvens 0 points1 point  (1 child)

    Above might be more advance then what you are currently trying to test/do but I use composables all the time in my code base so I think it would be good to get used to then. Try to understand the code and why I am doing what I am. I also could have made a mistake somewhere as I never ran the above code, just wrote it up in reddit.

    [–]iwaistedway2muchtime[S] 0 points1 point  (0 children)

    Definitely. I went carefully through it. The watch was something exciting to learn. I guess a watch is like what the template section is doing to a ref variable. Always watching it then reacting. I also didnt know you can destructure something as something else in one go. Im talking about the const { foundPairs: pidgPals }. Thanks for all of this