How to make pagination work? Async await function in vue.js 3 setup - vue.js

I was trying to make an app which lists a user's repositories from github using github API, however I'm having a big problem with fetching data from all pages (so far I can only get repos from one page). I tried to fix it by using an async/await function (instead of Promise), but it's also my first time using vue3 and I have no idea how to have a function inside of the setup() method.
The current code is here:
https://github.com/agzpie/user_repos
My try at using async/await, which didn't work:
import ListElement from "./components/ListElement";
import { ref, reactive, toRefs, watchEffect, computed } from "vue";
export default {
name: "App",
components: {
ListElement,
},
setup() {
const name = ref(null);
const userName = ref(null);
const state = reactive({ data: [] });
let success = ref(null);
const userNameValidator = /^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}$/i;
const split1 = reactive({ spl1: [] });
const split2 = reactive({ spl2: [] });
async function myFetch() {};
/*
* Check for input in the form and then fetch data
*/
watchEffect(() => {
if (!userName.value) return;
if (!userNameValidator.test(userName.value)) {
console.log("Username has invalid characters");
return;
}
let hasNext = false;
state.data = [];
do {
async function myFetch() {
let url = `https://api.github.com/users/${userName.value}/repos?per_page=5`;
let response = await fetch(url);
if (!response.ok) {
success.value = false;
throw new Error(`HTTP error! status: ${response.status}`);
}
success.value = true;
// check response.headers for Link to get next page url
split1.spl1 = response.headers.get("Link").split(",");
let j = 0;
while (j < split1.spl1.length) {
split2.spl2[j] = split1.spl1[j].split(";");
console.log(split2.spl2[j][0]);
console.log(split2.spl2[j][1]);
if (split2.spl2[j][1].includes("next")) {
let urlNext = split2.spl2[j][0].replace(/[<>(\s)*]/g, "");
console.log(urlNext);
url = urlNext;
hasNext = true;
break;
} else {
hasNext = false;
}
j++;
}
// second .then
let myData = await response.json();
state.data.push(...myData);
console.log("data", myData);
name.value = "";
}
myFetch().catch((err) => {
if (err.status == 404) {
console.log("User not found");
} else {
console.log(err.message);
console.log("oh no (internet probably)!");
}
});
} while (hasNext);
});
// Sort list by star count
const orderedList = computed(() => {
if (state.data == 0) {
return [];
}
return [...state.data].sort((a, b) => {
return a.stargazers_count < b.stargazers_count ? 1 : -1;
});
});
return {
myFetch,
success,
isActive: true,
name,
userName,
ListElement,
...toRefs(state),
orderedList,
};
},
};
Any help would be highly appreciated

The call to myFetch() near the end is a call to an async function without an await, so it is effectively going to loop (if hasNext was initialized to true, but it isn't) without waiting for it to complete.
You should probably change that line to await myFetch() and wrap it all with a try/catch block.
I also don't really care for the way you're directly updating state inside the async myFetch call (it could also be doing several of those if it looped) and perhaps it should be returning the data from myFetch instead, and then you can use let result = await myFetch() and then make use of that when it returns.
Also, instead of awaiting myFetch() result, you could not await it but push it onto a requests array and then use await Promise.all(requests) outside the loop and it is one operation to await, all requests running in parallel. In fact, it should probably be await Promise.allSettled(requests) in case one of them fails. See allSettled for more.
But also I wonder why you're reading it paged if the goal is to fetch them all anyway? To reduce load on the server? If that is true, issuing them paged but in parallel would probably increase the load since it will still read and return all the data but require multiple calls.

Related

React Native Hooks initializer not taking the correct value

What I am trying to do is sync a list of attendees from an online database, and if the current user is in the list, then disable a button, else enable the button.
I am using react native hook (I am not sure if I am using the term correctly as I am fairly new to react), in order to set the value of disabling the button.
The issue that I am facing is that the value is getting initialized to false, even tho it should clearly get initialized to true.
After adding some logging I made sure that the function is executing correctly and reaching the code where it sets the value to true.
const [buttonDisabled, changeButtonState] = useState( () => {
var database = firebase.database();
var userId = firebase.auth().currentUser.uid;
const dbRef = firebase.database().ref();
var Attendees = [];
var disable = false;
dbRef.child("gameAttendees").child(gameinfo.gameID).get().then((snapshot) => {
if (snapshot.exists()) {
Attendees = snapshot.val().Attendees;
for(var i=0;i<Attendees.length;i++){
if(Attendees[i]==userId){
return true;
}
}
} else {
console.log("no value");
return false;
}
}).catch((error) => {
console.error(error);
});
});
Adding an example of an async mount effect:
const Comp = () => {
const [s, setS] = useState(); // State will be undefined for first n renders
useEffect(() => {
// Call the async function and set the component state some time in the future
someAsyncFunction().then(result => setS(result));
}, []); // An effect with no dependencies will run only once on mount
return </>;
};

React-Native Async Storage returns weird object with correct value in two consecutive lines

I've researched this and not found a similar case answered, at my whits end.
I have the following code to get an item from AsyncStorage, which returns the correct value when I log it on the console, but the function's return value is: {\"_40\":0,\"_65\":1,\"_55\":\"Nick\",\"_72\":null}
const getData = async () => {
try {
const value = await AsyncStorage.getItem('userDetails');
if(value !== null) {
const wsi_user = await JSON.parse(value);
console.log("userName: " + wsi_user.userName); // returns "Nick"
return wsi_user.userName; // returns {\"_40\":0,\"_65\":1,\"_55\":\"Nick\",\"_72\":null}
}
} catch(e) {
letter = "D";
}
}
I've seen similar articles where people mention that the promise needs to resolve and I get that, but my result is within the outputted weird object, AND, my result is correct when logged to console the line before return.
Please advise. No clue how to fix this.
You can call this function like:
const result = await getData()
Also, you don't need the await before JSON.parse() as it is synchronous.
Maybe you can use react-native-easy-app, through which you can access AsyncStorage synchronously, and can also store and retrieve objects, strings or Boolean data
import { XStorage } from 'react-native-easy-app';
import { AsyncStorage } from 'react-native';
// or import AsyncStorage from '#react-native-community/async-storage';
export const RNStorage = { userInfo: undefined };
const initCallback = () => {
// From now on, you can write or read the variables in RNStorage synchronously
// equal to [ await AsyncStorage.setItem('userInfo',JSON.stringify({ name:'rufeng', age:30})) ]
RNStorage.userInfo = {name: 'rufeng', age: 30};
};
XStorage.initStorage(RNStorage, AsyncStorage, initCallback);
You need to convert the object to a string before saving it in the async storage AsyncStorage.setItem(key, JSON.stringify(data));
Example:
export const setStorageItem = async (key: string, data: any): Promise<void> => {
return AsyncStorage.setItem(key, JSON.stringify(data));
};
export const getStorageItem = async <T>(key: string): Promise<T | null> => {
const item = await AsyncStorage.getItem(key);
if (item) {
return JSON.parse(item);
}
return null;
};
Example from the docs

How to exit a while loop depending of the result of an Axios call?

In order to make sure a user-entered word exists, I need to keep prompting (while loop) for a word until the word API finds the word.
My question is: how can I exit the while loop depending of Axios call result?
Below is my code so far.
const wordApiBaseUrl = 'https://www.dictionaryapi.com/api/v1/references/sd4/xml'
while (true) {
const wordToGuess = prompt('Enter a word:').toLowerCase()
const endPointUrl = `${wordApiBaseUrl}/${wordToGuess}?key=${wordApiKey}`
this.axios.get(endPointUrl).then(res => {
if (res.data.includes('def')) {
break
}
})
}
Try this:
const wordApiBaseUrl = 'https://www.dictionaryapi.com/api/v1/references/sd4/xml'
const vm = this; // <--- assuming this is the Vue instance
const dispatcher = {
execute: function() {
const wordToGuess = prompt('Enter a word:').toLowerCase()
const endPointUrl = `${wordApiBaseUrl}/${wordToGuess}?key=${wordApiKey}`
const dispatcher = this;
vm.axios.get(endPointUrl).then(res => {
if (!res.data.includes('def')) {
dispatcher.execute();
}
})
}
}
dispatcher.execute();
Rather than using a while loop or using an async/await you can use recursion in our promise. If the result is not satisfied, re-run the AJAX call.

How to combine a loop of Axios calls with an await-ed function?

That's a tricky one I guess. I'm prompting user for a word which I validate with an Axios API call. Once validation clears, the main loop of my game - hangman - starts with a wait between each move (hence the use of await).
Problem: in current version, the main game loop (starting after "once validation clears, game starts below" comment) must starts after validation, when in fact it starts at the same time which messes everything up.
And I can't put my main loop inside the then() part of my Axios call because in that case the await-ed function call ceases to work.
Any idea to get out of this mess?
async startGameComputerGuesser () {
var wordIsValidated = false
const vm = this
const dispatcher = {
execute: function () {
const wordApiBaseUrl = 'https://www.dictionaryapi.com/api/v1/references/sd4/xml'
wordToGuess = prompt('Enter a word:').toLowerCase()
const dispatcher = this
vm.axios.get(`${wordApiBaseUrl}/${wordToGuess}?key=${wordApiKey}`).then(res => {
if (!res.data.includes('def')) {
dispatcher.execute()
} else {
wordIsValidated = true
}
})
}
}
dispatcher.execute()
// once validation clears, game starts below
if (wordIsValidated) {
while (!this.$store.state.gameIsOver) {
await this.resolveAfter2Seconds()
// main loop of the game goes here
}
}
}
use await inside the execute and return true/false then use while to check that condition like below
async startGameComputerGuesser() {
let wordIsValidated = false;
const vm = this;
const dispatcher = {
async execute() {
const wordApiBaseUrl = 'https://www.dictionaryapi.com/api/v1/references/sd4/xml'
const wordToGuess = prompt('Enter a word:').toLowerCase();
const res = await vm.axios.get(`${wordApiBaseUrl}/${wordToGuess}?key=${wordApiKey}`);
return res.data.includes('def');
}
}
// validation
while (!wordIsValidated) {
wordIsValidated = await dispatcher.execute();
}
// game starts below
while (!this.$store.state.gameIsOver) {
await this.resolveAfter2Seconds()
// main loop of the game goes here
}
}
Example code:
const startGameComputerGuesser = async function() {
let wordIsValidated = false;
const dispatcher = {
async execute() {
const res = await new Promise(res => setTimeout(() => res(Math.floor(Math.random() * 10)), 500));
console.log(res);
return res == 6;
}
}
// validation
while (!wordIsValidated) {
wordIsValidated = await dispatcher.execute();
}
// once validation clears, game starts below
console.log('started');
}
startGameComputerGuesser();

how to get multiple fields from expo SecureStore

I am new to ES6 and react-native, trying to get multiple values from the SecureStore.
I think I am misunderstanding promises here ... global.userData is empty in the Promise.all(promises).then function. The relevant values do exist in the secure store
My code is:-
getUserData(fields) {
var promises = [];
var that = this;
global.userData = {};
function getField(field) {
return SecureStore.getItemAsync(field)
.then(res => {
console.log(field+"="+res); // this appears after the log below
global.userData[field] = res;
})
.catch(error => {
global.userData[field] = null;
});
}
fields.map(field => {
promises.push[getField(field)];
});
Promise.all(promises).then(function(v) {
console.log(global.userData); // this is empty
that.setState({ isReady: true }); // allow page to render
});
}
getUserData(["userId", "userName","etc"]);
My bad ... inadvertantly used
promises.push[getField(field)];
should have been:
promises.push(getField(field));
Suprised it wasn't detected as a syntax error ...