vue3 props passed to child component are undefined - javascript

Im struggling with passing data to my child component.
On level of parent componenet, I'm doing nested axios call to retrieve the data. Then I pass this data to my child component. Data is correct, but child component sees it as undefined.
It looks like the child component is rendered before the actual call is finished.
parent:
<template>
<User_Menu v-if="user_info" :user="user_info" :user_tickets="user_tickets"/>
</template>
<script setup lang="ts">
import User_Menu from "#/components/User/User_Menu.vue"
import { useRoute } from 'vue-router'
import { ref, reactive, onMounted } from 'vue'
import { UserQrInstance } from '#/interfaces/user';
import { UserTickets } from '../interfaces/tickets';
import { post_call, get_call} from "#/api"
import { error_notification } from "#/error_notifications"
const route = useRoute()
const user_id = ref(0)
const user_info = reactive({} as UserQrInstance)
const user_tickets = reactive([] as UserTickets)
const get_user_tickets = async (qr: string) => {
try
{
const response = await post_call('tickets/get_tickets', {qr_code: qr})
Object.assign(user_tickets, response.data)
}
catch (e){
error_notification(e)
}
}
const get_user = async () => {
try
{
const response = await get_call(`users/find_user/${user_id.value}`)
Object.assign(user_info, response.data)
await get_user_tickets(response.data.qr_code)
}
catch (e){
error_notification(e)
}
}
onMounted(() => {
user_id.value = route.params.id
get_user()
})
</script>
child:
<template>
{{props.user}}
</template>
<script setup lang="ts">
const props = defineProps<{user: Ref<UserQrInstance>, user_tickets: Ref<UserTickets>}>()
console.log(props.user) -> ok
console.log(props.user.name) -> undefined
</script>
How can I make it working? Some delay or something?

Related

Can't set Ref data property in Vue3, item undefined

Essentially I'm using Vue3 and Nuxt3. I'm trying to grab the :id param from the URL and if it matches an ID in a JSON it updates a ref data point called 'exists'. My console log shows all that is working. The issue is this.exists is showing as undefined. I attempted the const self = this; trick, to no avail.
Full warning:
500: Cannot set properties of undefined (setting 'exists')
<template>
<div class="single-hunt">
<div class="container">
Single Hunt
</div>
</div>
</template>
<script>
import hunt from '../../assets/api/hunts.json'
import { onMounted } from 'vue';
import { useRoute } from 'vue-router'
export default {
setup() {
const route = useRoute();
const id = route.params.id;
const exists = ref(false);
onMounted(() => {
for (let i = 0; i < hunt.length; i++) {
if (hunt[i].address == id) {
console.log('exists')
this.exists = true;
break;
}
}
})
return {
hunt,
exists
}
}
}
</script>
<styles src="../../assets/css/singlehunt.css"/>
You should import ref from vue: import { ref } from 'vue';
https://vuejs.org/guide/essentials/reactivity-fundamentals.html#reactive-variables-with-ref

useReactVar in apolloLink cause cannot update component while rendering a diffrend component

i use apollo client and react in project
i want appear loading indicator while network request, so i set react variables and change it inside apollo link
but it cause cannot update component(Indicator) while rendering a diffrend component(component that call useQuery hook)
loadingLink.ts
import { ApolloLink } from '#apollo/client';
import { loadingVar } from 'gql/store/reactiveVariables';
export const loadingLink = new ApolloLink((operation, forward) => {
loadingVar(true);
return forward(operation).map(data => {
loadingVar(false);
return data;
});
});
loadingIndicator.ts
import React from 'react';
import { useReactiveVar } from '#apollo/client';
import { loadingVar } from 'gql/store/reactiveVariables';
import { Indicator } from './Indicator';
const LoadingIndicator: React.FC = () => {
const loading = useReactiveVar(loadingVar);
if (!loading) return null;
return <Indicator />;
};
export default LoadingIndicator;
error message
error message
BatchList.tsx
const BatchList = () => {
const {data, error} = useQuery(~~~);
if(error) return null;
if(loading || !data) return null;
return ~~~~
}
in documentation, they say should use useEffect hook but inside ApolloLink i can't use useEffect hook
How can i fix it

Vue JS/Vuex - Getter ID not updating with route param ID

I'm getting the ID of a user based on the ID I pass in my route params. When I first load the page and access one of the users, the getter displays the ID from the route param accordingly, however once I go back, and click on another user, the ID from the param does not match with the getter. Instead the getter shows the ID of the previously accessed user. Can anyone kindly suggest a solution for this?
setup() {
const store = vuexStore;
const adminId = router.currentRoute.params.adminId;
console.log("ID param:", adminId);
getSelectedAdmin();
const selectedAdmin = computed(() => store.getters.getSelectedAdmin);
console.log("getter Id:", selectedAdmin.value.id);
function getSelectedAdmin() {
return store.dispatch(GET_ADMIN_BY_ID, adminId)
}
return {
selectedAdmin,
}
}
You should fetch the user in the onMounted hook.
(I use the script setup, vue-router#next & vuex#next)
<script setup>
import { ref, onMounted, computed } from 'vue'
import { useRoute } from 'vue-router'
import { useStore } from 'vuex'
const store = useStore()
const route = useRoute()
const selectedAdmin = computed(() => store.getters['getSelectedAdmin'])
onMounted(async () => { await store.dispatch('GET_ADMIN_BY_ID', route.params.adminId) })
</script>
<template>
<div v-if="selectedAdmin">
{{ selectedAdmin.username }}
</div>
<div v-else>Loading ...</div>
</template>
In your store :
Set initial value for selectedAdmin to null
In the GET_ADMIN_BY_ID action, reset selectedAdmin to null before updating
const state = {
currentAdmin: null,
}
const mutations = {
SET_ADMIN: (state, payload) => state.currentAdmin = payload
}
const getters = {
getSelectedAdmin: (state) => state.currentAdmin
}
const actions = {
GET_ADMIN_BY_ID: ({ commit }, id) => {
commit('SET_ADMIN', null)
return axios.get(`admin/${id}`)
.then((response) => { commit('SET_ADMIN', response.data) })
.catch((err) => { commit('SET_ADMIN', null) })
}

Too many re-renders. React limits the number of renders to prevent an infinite loop when passing states?

I am trying to access state from one component to another
FetchCompo.js
// import React from "react";
import React, { useState, useEffect } from "react";
//more imports
const FetchUserItems= () => {
//some state
const [userFirstName, setUserFirstName] = useState("");
const [userItem, setUserItem] = useState([]);
let userName = //somecode
setUserFirstName(userName);
let userItemsData= userData.MyArray.items;
if (userItemsData.length === 0) {
const emptyItems = [
{
//obj data
},
];
setUserItem(emptyItems );
} else {
//someData
setUserItem(userItemsData);
}
return { userFirstName, userItem};
};
export default FetchCompo;
I wanted to use userFirstName, userItem in the another Test.js component.
// import React from "react";
import React, { useState, useEffect } from "react";
import FetchCompofrom "../myFunctions/FetchCompo";
//more imports
const Test = () => {
//Wanted to use userFirstName in Test.js component
const { userFirstName, userItem } = FetchCompofrom();
return (
<div>{userFirstName}</div>
)
}
when I am trying to get the userFirstName, userItem in the Test.js component then getting error of Too many renders
looking for a solution how i can access these state userFirstName, userItem form one component to another.
You're actually importing the React Component not the FetchUserItems helper function...
import FetchCompofrom "../myFunctions/FetchCompo";
But you could do something like...
const [userFirstName, setUserFirstName] = useState('');
const [userItem, setUserItem] = useState([]);
const FetchUserItems = () => {
/**
* Make it plain helper function for fetching userItems
* Do-not set-state here...
*/
return { userFirstName, userItem };
};
export const FetchUserItems;
/** In your component ... say in useEffect */
const result = FetchUserItems();
/** setState here in case of result */
In Test.js
import { FetchUserItems } "../myFunctions/FetchCompo";
Your are using setUserFirstName(userName) in FechUserItems, outside useEffect or normal function, this will provoque the component to re-render indefinitely because setting the states provoques re-rendering.
I would suggest to make FetchUserItems a normal function, because you are not rendering anything in it. You could use only Test comp for it.
The Test comp would be something like this:
// import React from "react";
import React, { useState, useEffect } from "react";
import FetchCompofrom "../myFunctions/FetchCompo";
//more imports
const Test = () => {
const [userFirstName, setUserFirstName] = useState("");
const [userItem, setUserItem] = useState([]);
useEffect(() => fetchUserFirstName, [])
const fetchUserFirstName = () => {
// your code here and
// setUserFirstName in the end
}
//Wanted to use userFirstName in Test.js component
const { userFirstName, userItem } = FetchCompofrom();
return (
<div>{userFirstName}</div>
)
}

How to wait for server response before calling Apollo Graph QL Query?

I'm attempting to call a Graph QL Query after receiving data from my useEffect hook. I need the data from the response to use in the Query. Hooks however cannot be called conditionally. If I take away the condition however, loadedAnime will be undefined. How do I get around this restraint?
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import AnimeBanner from "../components/AnimeBanner";
import { useHttpClient } from "../Hooks/http-hook";
import { GetAnimeData } from "../GraphQLFunctions";
import { useQuery } from "#apollo/react-hooks";
import gql from "graphql-tag";
const GET_ANIME_INFO = gql`
query GetAnimeInfo($name: String!) {
Media(search: $name) {
title {
romaji
english
native
userPreferred
}
episodes
id
bannerImage
}
}
`;
const Anime = (props) => {
//Logic for getting anime data from mongoDB (episodes, name, cover image)
const { isLoading, error, sendRequest } = useHttpClient();
const [loadedAnime, setloadedAnime] = useState();
const URLTitle = useParams().URLTitle;
useEffect(() => {
const fetchAnime = async () => {
try {
const responseData = await sendRequest(
"http://localhost:5000/api/anime/" + URLTitle
);
setloadedAnime(responseData.animeData[0]);
} catch (err) {
console.log(err);
}
};
fetchAnime();
}, [sendRequest, URLTitle]);
if (isLoading || error) {
return null;
}
//Logic for getting anime data from anilist (Descriptions, tags, banner, trailer, etc.)
const { apiData, apiLoading, apiError } = useQuery(GET_ANIME_INFO, {
variables: {
name: loadedAnime.anime_name,
},
});
if (apiLoading || apiError) {
return null;
}
return <AnimeBanner src={apiData.Media.bannerImage} />;
};
export default Anime;
Short Answer: You can checkout useLazyQuery instead of useQuery.
Documentation link: https://www.apollographql.com/docs/react/data/queries/#executing-queries-manually
When React mounts and renders a component that calls the useQuery hook, Apollo Client automatically executes the specified query. But what if you want to execute a query in response to a different event, such as a user clicking a button?
The useLazyQuery hook is perfect for executing queries in response to events other than component rendering. This hook acts just like useQuery, with one key exception: when useLazyQuery is called, it does not immediately execute its associated query. Instead, it returns a function in its result tuple that you can call whenever you're ready to execute the query
import React, { useState } from 'react';
import { useLazyQuery } from '#apollo/client';
function DelayedQuery() {
const [dog, setDog] = useState(null);
const [getDog, { loading, data }] = useLazyQuery(GET_DOG_PHOTO);
if (loading) return <p>Loading ...</p>;
if (data && data.dog) {
setDog(data.dog);
}
return (
<div>
{dog && <img src={dog.displayImage} />}
<button onClick={() => getDog({ variables: { breed: 'bulldog' } })}>
Click me!
</button>
</div>
);
}
You can either call the query after the await finishes or you can call your query in another useEffect once you update state after your api call. In general, something like this,
const [state, setState] = useState({})
useEffect(async () => {
const result = await get('/api/blah-blah-blah')
// run your query here now that the await has resolved
}, [someDependency])
or
const [state, setState] = useState({})
useEffect(async () => {
const result = await get('/api/blah-blah-blah')
setState(result)
}, [someDependency])
useEffect(() => {
if(state.id) {
// run the query
}
}, [state.someProp])

Categories