Vue3 - build API url and fetch data after route changed - javascript

I am trying to display the borders of a country from restcountries api (https://restcountries.eu/) as clickable buttons.
this is how I try to build the url for the api
mounted() {
axios
.get(this.urlDetail)
.then(response => (
this.details = response.data[0]
))
this.borders = this.details.borders.join(";");
this.urlBorders = "https://restcountries.eu/rest/v2/alpha?codes=" + this.borders;
fetch(this.urlBorders)
.then(res => res.json())
.then(data => this.bordersData = data)
the problem is, that the details array is empty at that moment. When I reload the page, the data is fetched correctly.
I tried to:
use beforeMount()
use a isFetching boolean
get the data with #click-function
tried is with these function in mounted():
document.onreadystatechange = () => {
if (document.readyState == "complete") {
console.log('Page completed with image and files!')
}
}
this is my data:
data() {
return {
isFetching: true,
urlDetail: 'https://restcountries.eu/rest/v2/name/' + this.$route.params.countryName,
details: [],
borders: "",
urlBorders: "",
bordersData: []
}
this is the relevant html snipped to display the buttons:
<p><strong>Border Countries:</strong><button class="border-button" v-for="border in bordersData"><router-link :to="{ name: 'border', params: {borderName: border.name}}">{{ border.name }}</router-link></button></p>
thanks for helping!

Try to wait for responses:
methods: {
async getBorders() {
await axios
.get(this.urlDetail)
.then((response) => (this.details = response.data[0]));
},
setBorders() {
this.borders = this.details.borders.join(";");
this.urlBorders =
"https://restcountries.eu/rest/v2/alpha?codes=" + this.borders;
},
async getDets() {
await axios
.get(this.urlBorders)
.then((response) => (this.bordersData = response.data));
},
},
},
async mounted() {
await this.getBorders();
this.setBorders();
await this.getDets();
},

Related

How to implement server side search filter in redux tool kit using query builder RTK?

I want to apply server side search filter by text using redux toolkit.
I have two query builder methods in place. One for fetching all items and second for fetching only filtered data.
Query builder for fetching all items is
getAllBlogs: builder.query<BlogType[], void>({
queryFn: async () => {
const collectionRef = collection(Firestore, BLOG_COLLECTION)
const q = query(collectionRef, limit(1000))
const resp = await getDocs(q)
return {
data: resp.docs.map((doc) => doc.data() as BlogType),
}
},
providesTags: (result) => {
const tags: { type: 'Blogs'; id: string }[] = [
{ type: 'Blogs', id: 'LIST' },
]
if (result) {
result.forEach(({ id }) => {
tags.push({
type: 'Blogs',
id,
})
})
}
return tags
},
}),
This works fine and I'm getting the whole list through useGetAllBlogsQuery data.
Query builder for fetching filtered data is here: (Partially completed)
getBlogsByTitle: builder.query<BlogType[], string>({
queryFn: async (title) => {
const collectionRef = collection(Firestore, BLOG_COLLECTION)
const q = query(
collectionRef,
where('searchIndex', 'array-contains', title),
limit(1000),
)
const resp = await getDocs(q)
return {
data: resp.docs.map((doc) => doc.data() as BlogType), // Correct data
}
},
// I'm trying to only push the resultant items in state. This is not working
providesTags: (result) => {
const tags: { type: 'Blogs'; id: string }[] = []
if (result) {
result.forEach(({ id }) => {
tags.push({
type: 'Blogs',
id,
})
})
}
return tags
},
}),
I have react component looks like this where I'm calling these queries.
const Blogs: NextPage = () => {
const { data: blogs } = blogsApi.useGetAllBlogsQuery()
const [getBlogsByTitle] = blogsApi.useLazyGetBlogsByTitleQuery()
const debounced = useDebouncedCallback(async (value) => {
const { data } = await getBlogsByTitle(value)
console.log(data) // Correct data
}, 500)
return (
<div>
<InputText
onChange={(e) => debounced(e.target.value)}
/>
</div>
)}
The above code has two functionalities.
Fetch all the items on initial load.
Filter when debounced function is being called.
What I want is when getBlogsByTitle is called it will auto update the same state blogs in redux and we don't have to do much.
We are getting correct response in getBlogsByTitle but this query is not updating state with only its filtered response.
I'm new to redux-toolkit. Can someone help me out here where am I doing wrong ?

React - State is not updating when it is supposed to, why is react doing this? (not retaining)

Hi so I'm trying to grab some json from an api and then populate a table, pretty simple stuff.
What's happening is that I can see the "tableData" state being updated as each new row comes in, I'm also logging every time "tableData" is updated, yet maybe .5 seconds after its all done my "tableData" is empty again (check console screenshots)
const [bigChartData, setbigChartData] = React.useState("data1");
const [tableData, setTableData] = React.useState([]);
const setBgChartData = (name) => {
setbigChartData(name);
};
const getData = () => {
axios.get("URL")
.then(res => {
const data = res.data.items.forEach(item => {
setTableData(oldData => [...oldData, {
data: [
{ text: item.title },
{ text: "asd" + item.url },
{ text: "some links..." }
]
}]);
});
})
.catch(err => console.log(err));
setTimeout(function () {
console.log(tableData);
}, 3000);
}
useEffect(() => {
getData();
}, []);
useEffect(() => {
console.log("Table data updated:");
console.log(tableData);
}, [tableData]);
I think you should not iterate through each row inside getData() method instead try following code
const getData = () => {
axios.get("URL")
.then(res => {
const data = res.data.items.map(item => {
return{
data: [
{ text: item.title },
{ text: "asd" + item.url },
{ text: "some links..." }
]
};
});
setTableData(data)
}).catch(err => console.log(err));
}
or if you have already some data in tableData then
setTableData([...tableData, data])

ApolloGraphQL not triggering query when landing on page after a mutation that changes data

Intended result
I have two routes: /test/ and /test/:id.
On /test/ I have a list of events and it's only made of events that haven't been resolved
On /test/:id I have a mutation to mark an event as resolved, and, on success, I'm redirecting the user back to /test/.
This success means that the event should no longer appear on /test/ and I'm expecting a new request to get the list of events.
// my file with the mutation
const [eventResolveMutation] = useEventResolveMutation({
onCompleted: () => {
showSuccessToast(
`${t('form:threat-resolved')}! ${t('general:threat')} ${t(
'form:has-been-resolved'
)}.`
)
setTimeout(() => {
navigate('/threats/live')
}, 2000)
},
onError: (error: ApolloError) => {
showErrorToast(error.message)
},
})
const handleEventResolveClick = (id: string) => {
eventResolveMutation({ variables: { id: id, isResolved: true } })
}
return (
<button onClick={() => handleEventResolveClick(id)}>Press</button>
)
// my file with the `events` query
// the results are displayed in a table, which is way I have `currentPage` and `pageSize` in them
const [getEvents, { loading, data }] = useEventsLazyQuery()
useEffect(() => {
getEvents({
variables: {
page: currentPage,
pageSize: paginationSizeOptions[chosenDropdownIndex],
isThreat: true,
isResolved: false,
},
})
}, [chosenDropdownIndex, currentPage, getEvents])
Actual outcome:
Once I press the button that triggers the mutation and I'm redirected to the /tests, I can see that I'm landing inside the useEffect by logging something. What I don't see is a request made via getEvents, which is expected to happen since all the functionalities with the page work
Extra info:
// my graphqlclient.ts
import {
ApolloClient,
ApolloLink,
createHttpLink,
InMemoryCache,
} from '#apollo/client'
const serverUrl = () => {
switch (process.env.REACT_APP_ENVIRONMENT) {
case 'staging':
return 'env'
case 'production':
return 'env'
default:
return 'env'
}
}
const cleanTypeName = new ApolloLink((operation, forward) => {
if (operation.variables) {
const omitTypename = (key: string, value: any) =>
key === '__typename' ? undefined : value
operation.variables = JSON.parse(
JSON.stringify(operation.variables),
omitTypename
)
}
return forward(operation).map((data) => data)
})
const httpLink = createHttpLink({
uri: serverUrl(),
credentials: 'include',
})
const httpLinkWithTypenameHandling = ApolloLink.from([cleanTypeName, httpLink])
const client = new ApolloClient({
link: httpLinkWithTypenameHandling,
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: {
fetchPolicy: 'cache-and-network',
},
},
})
export default client
// my mutation
// this mutation will mark an `id` as `resolved` and that means that it should disappear from the list above
mutation EventResolve($id: ID!, $isResolved: Boolean!) {
eventResolve(id: $id, isResolved: $isResolved) {
id
sequence
}
}

Why API call no longer works since a refactor?

I made this useEffect code that works good in a screen:
useEffect(() => {
getDatasFromId(searchId)
.then((data) => {
const returnObject = {
next: data.next,
items: data.results.map(item => ({ name: item.name, url: item.url }))
};
setHasNext(returnObject.next);
setDataToDisplay(returnObject.items);
});
}, [searchId]);
But now, I made some modification and the API call is running without return (no error or whatever). My new code is this:
useEffect(() => {
const apiUrl = getApiUrl(searchId);
console.log("API URL : ", apiUrl);
getDatas(apiUrl)
.then((data) => {
console.log("We get some data : ", data.results );
const returnObject = buildDataObject(data);
console.log("returnObject : ", returnObject);
setDataToDisplay(returnObject.items);
returnObject.next !== null && loadMoreApiDatasIf(returnObject.next);
});
}, [searchId]);
I don't see the error in the new code.

Can't set state in react

So, I'm simply trying to set state in my react app. Simply get data from Axios, and then set state. But no matter what I do, the state will not set. I've tried putting it in a callback since it's async and putting it my component did mount and component did update alas nothing. any pointers?
class App extends Component {
componentDidUpdate() {}
constructor(props) {
super(props);
this.state = {
Catogories: [
"Business",
"Entertainment",
"General",
"Health",
"Science",
"Sports",
"Technology"
],
CatPics: [],
TopStories: [],
Selection: [],
Sources: [],
selected: false
};
}
GeneratePic = () => {
this.state.Catogories.forEach(Catogory => {
axios
.get(
"https://api.pexels.com/v1/search?query=" +
Catogory +
"&per_page=15&page=1",
{
Authorization:
"563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
var object = { Catogory: res.photos[0].src.large2x };
this.state.CatPics.push(object);
});
});
};
dump = x => {
this.setState({ TopStories: x }, console.log(this.state.TopStories));
};
TopStories = () => {
console.log("working");
axios
.get(
"https://newsapi.org/v2/top-headlines?country=us&apiKey=91bec895cf8d45eaa46124fb19f6ad81"
)
.then(res => {
console.log(res);
const data = res.data.articles;
console.log(data);
this.dump(data);
});
};
You are doing two things wrong.
Don't mutate the state
Don't do async actions inside loop and then use same loop variable inside async callback because at that point in time, loop variable will have some other value and not the respective iteration category.
GeneratePic = async () => {
const promises = this.state.Catogories.map(Catogory => {
return axios
.get(
"https://api.pexels.com/v1/search?query=" +
Catogory +
"&per_page=15&page=1",
{
Authorization:
"563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
return res.photos[0].src.large2x;
});
});
let photos = await Promise.all(promises);
photos = this.state.Catogories.map((cat, index) => ({ [cat]: photos[index] }));
this.setState({ CatPics: photos });
};
getPics = cat => {
return axios
.get(
"https://api.pexels.com/v1/search?query=" +
cat +
"&per_page=15&page=1",
{
Authorization:
"563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
return { [cat]: res.photos[0].src.large2x };
});
}
GeneratePic = async () => {
const promises = this.state.Catogories.map(Catogory => {
this.getPics(Catogory);
});
let photos = await Promise.all(promises);
this.setState({ CatPics: photos });
};
This should work.
Dont Mutate the state.
GeneratePic = () => {
this.state.Catogories.forEach(async Catogory => {
await axios
.get(
"https://api.pexels.com/v1/search?query=" +
Catogory +
"&per_page=15&page=1", {
Authorization: "563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
var object = { Catogory: res.data.photos[0].src.large2x };
const cPics = [...this.state.CatPics];
cPics.push(object);
this.setState({
CatPics: cPics
})
});
});
};

Categories