Async data fetching not updating reactive data property - vue.js

Ok, guys, I´m having a little issue today, all day long, trying to solve, the deal goes like this...
I´m fetching some data from firebase to render on the html template with asynchronous functions
I have a fetchList Method that is like this:
async mounted() {
let ret = await this.fetchJobRequireList()
console.log('fetchjoblist' , ret)
async fetchJobRequireList() {
// debugger
let services = JSON.parse(sessionStorage.getItem('required_services'))
services != null ? this.required_services = services : null
let docs_ = []
let result = []
if (!services) {
// this.required_services = []
// get required services per user id
let collections = this.$options.firebase.functions().httpsCallable('getRequiredServices')
let docs = await this.$options.firebase.firestore().collection('required_services').get()
// console.log('required services docs', docs)
let _ = this
for (let doc of docs.docs) {
result[doc.id] =
await collections({doc_id: doc.id}).then( async r => {
// debugger
let collections_ = r.data.cols
docs_ = []
_.required_services[doc.id] = []
for (let collection of collections_) {
let path = collection._referencePath.segments
// let documents =
let __ = _
await this.$options.firebase.firestore().collection(path[0])
.doc(path[1]).collection(path[2]).get()
.then(async documents => {
// console.log('__documents__', documents)
for (let doc_ of documents.docs) {
doc_ = await documents.docs[0].ref.get()
doc_ = {
id: doc_.id,
path: doc_.ref.path,
data: doc_.data()
}
// debugger
__.required_services[doc.id].push(doc_)
console.log("this?", this.required_services[doc.id], '__??', __.required_services)
docs_.push(doc_)
}
})
}
console.log('__docs__', docs_)
return docs_
}).catch(err => console.error(err))
// console.log('this.required_services', this.required_services)
}
}
// console.log('object entries', Object.entries(result))
// console.log('__this.required_services__', Object.entries(this.required_services))
// sessionStorage.setItem('required_services', JSON.stringify(this.required_services))
return result
}
The expected result would be for the data function properties to be update after the firebase response came, but no update is happening.
If anyone, have any clues, of what could be happening... some people told me that asynchrounous functions could cause problems... but there is no alternative for them, I guess...

This line
_.required_services[doc.id] = []
is not reactive. See the first point in the docs

So as pointed by #StephenThomas, there is some limitations in array change detection capabilities in reactive property usage.
So after loading the content from firebase, try to push it like this.joblist.push(doc) vue property will not react properly and make some confusion in the head of someone that doesn´t know about this limitation in detecting this kind of array mutation (https://v2.vuejs.org/v2/guide/list.html#Caveats)...
By using this line, now is possible to see the changes in property inside the Vue dev tools
_.joblist.splice(0,0, local_doc)
Thanks #SthephenThomas, for pointing this out!!

Related

Shopify Storage Redis Issue with Node React App

I have added session storage in serve.js as follows :-
import SessionHandler from "./SessionHandler";
const sessionStorage = new SessionHandler();
Shopify.Context.initialize({
API_KEY: process.env.SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
SCOPES: process.env.SCOPES.split(","),
HOST_NAME: process.env.HOST.replace(/https:\/\//, ""),
API_VERSION: ApiVersion.October21,
IS_EMBEDDED_APP: false,
// This should be replaced with your preferred storage strategy
//SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
SESSION_STORAGE: new Shopify.Session.CustomSessionStorage(
sessionStorage.storeCallback,
sessionStorage.loadCallback,
sessionStorage.deleteCallback
),
});
My router get function is
router.get("(.*)", async (ctx) => {
const shop = ctx.query.shop;
let documentQuery = { shop: shop };
let data = await SessionStorage.findOne(documentQuery); //this finds the store in the session table
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
if (data == null) {
ctx.redirect(`/auth?shop=${shop}`);
} else {
await handleRequest(ctx);
}
} else {
await handleRequest(ctx);
}
});
and than in the SessionHandler file added code as attached in file ,
but when I run install the app it goes to the storeCallback , loadcallback and deletecallback function multiple times
StoreCallback Function Code
Load and delete callback function code
sorry I have edited my answer as I think its incorrect . all I can say for now is to look at this example:https://github.com/Shopify/shopify-api-node/blob/main/docs/usage/customsessions.md
if you havent already..

Vue Reading in parallel in a loop

I have an Set array which contains multiple Ids.I would like to loop through the Set and make the api calls in parallel for each id and get back the user object, add it to a map.How can i achieve it.
The value is Set
userIds :Set[2]
0:"1"
1:"2"
data() {
return {
userIds: new Set(),
};
},
const res = getUsers(userId)
hope it will resolve your issues. i did not test, just writing code here directly.
// set requests
let allRequests = []
//you can use other loop based on your decession
this.userIds.forEach(id => { allRequests.push(axios.get(`your_url\${id}`)) })
// you can use await it is based on you requirement
axios.all(allRequests).then(axios.spread((...responses) => {
//make your map here using responses
})).catch(errors => {
// react on errors.
})
you can check this reference

Using async await on promise in svelte (#await) is not returning the desired data that is formatted in a later function call

I am currently working with a API that does not return JSON. To get around this, I take the response and push it to a array ( while formatting it to remove any indentation and split each number in the response ). I then use this array of 183 numbers and run a for loop against an array with 183 characters to generate an object ( with custom key value pairs ) from the response.
Where things get confusing is when I start to use the data in my HTML. Usually you can just say <p>{data.overallRank}</p> but I am getting the error that the object is undefined. This makes sense because the data = {} was not created until the function ran.
After searching for a solution, I cam across svelte await blocks. You can read on them here and look at the tutorial : https://svelte.dev/tutorial/await-blocks
After trying to implement this feature, I have the following code.
let playerStats = []
let proxy = "https://cors-anywhere.herokuapp.com/"
let url = proxy + "https://secure.runescape.com/m=hiscore_oldschool/index_lite.ws?player=Hess"
const data = {};
let promise = getPlayer();
async function getPlayer() {
return await fetch(url).then((response) => response.text())
.then((data) => {
return data;
});
}
getPlayer().then((playerData) => {
// format data
playerStats.push(playerData.replace(/\n/ig, ",").split(','));
console.log(playerStats);
// Begin object generation
// names array shortened
let names = ["overallRank", "overallLvl", "overallXP", "attRank", ]
const data = {};
for (var i = 0; i < playerStats[0].length; i++) {
data[names[i]] = playerStats[0][i];
}
console.log(data);
});
<main>
{#await promise}
<p>Search for a Player...</p>
{:then data}
<p>The data is {data}</p>
{/await}
</main>
I suggest throwing this code in a svelte editor which you can find here: https://svelte.dev/tutorial/await-blocks
The issue with this code is that it is printing out the data from the return data, which returns the unformatted data and not the object.
I want to return the object that is created after the second function getplayer().then()... so I can use that object throughout my HTML.
I hope I explained things well and thank you in advance for any help.
It is returning the formatted data because that what is returned by the promise function. In order to get the formatted data, you have to add the formatting to the chain of promise
async function getPlayer() {
return await fetch(url)
.then((response) => response.text())
.then((playerData) => {
// here your transformation
// do not forget to actually return something
return data;
});
You were actually very close to sorting it out, just a bit of confusion regarding how promises work I believe.
All you need to do is format your data within the block where the data is handled following the fetch & decode operations:
async function getPlayer() {
return await fetch(url)
.then((response) => response.text())
.then((data) => {
return formatData(data);
});
}
Your formatData() function is essentially there already, you just need minor changes in your code:
function formatData(playerData) {
playerStats.push(playerData.replace(/\n/ig, ",").split(','));
console.log(playerStats);
// Begin object generation
// names array shortened
let names = ["overallRank", "overallLvl", "overallXP", "attRank", ]
const data = {};
for (var i = 0; i < playerStats[0].length; i++) {
data[names[i]] = playerStats[0][i];
}
console.log(data);
return data;
}
Finally, you do not need to explicitly declare a promise to use it in an {#await} block, you know getPlayer() returns a promise, so you can directly use that instead:
<main>
{#await getPlayer()}
<p>Search for a Player...</p>
{:then data}
<p>Overall Rank: {data.overallRank}</p>
{/await}
</main>
See functioning REPL

Fastest way to copy all realm data into JS object?

I'd like to populate my redux store with my model state stored in Realm on app start. (As suggested here Realm & React Native - Best practice to implement auto-updates?)
What is the fastest way to do so? Currently I am using reduce to create my JS object. Takes around 2 seconds in production for 2400 items. Is there a faster way?
const getRealmData = () => {
let items = db.objects('Item')
let allItems = items.reduce((r,v) => {
r[v.item_id]= {
article: v.article,
excerpt: v.excerpt,
given_title:v.given_title,[...]
}
return r
}, {})
return Immutable.fromJS(allItems)
}
Would you try like below ?
var plainResults = Array.prototype.map.call(items, (item) => {
var object = {};
for (var property of ItemSchema.properties) {
object[name] = item[name];
}
return object;
});

Can I use Ext's loader to load non-ext scripts/object dynamically?

In my ExtJS 4.0.7 app I have some 3rd party javascripts that I need to dynamically load to render certain panel contents (some fancy charting/visualization widgets).
I run in to the age-old problem that the script doesn't finish loading before I try to use it. I thought ExtJS might have an elegant solution for this (much like the class loader: Ext.Loader).
I've looked at both Ext.Loader and Ext.ComponentLoader, but neither seem to provide what I'm looking for. Do I have to just "roll my own" and setup a timer to wait for a marker variable to exist?
Here's an example of how it's done in ExtJS 4.1.x:
Ext.Loader.loadScript({
url: '...', // URL of script
scope: this, // scope of callbacks
onLoad: function() { // callback fn when script is loaded
// ...
},
onError: function() { // callback fn if load fails
// ...
}
});
I've looked at both Ext.Loader and Ext.ComponentLoader, but neither
seem to provide what I'm looking for
Really looks like it's true. The only thing that can help you here, I think, is Loader's injectScriptElement method (which, however, is private):
var onError = function() {
// run this code on error
};
var onLoad = function() {
// run this code when script is loaded
};
Ext.Loader.injectScriptElement('/path/to/file.js', onLoad, onError);
Seems like this method would do what you want (here is example). But the only problem is that , ... you know, the method is marked as private.
This is exactly what newest Ext.Loader.loadScript from Ext.4-1 can be used for.
See http://docs.sencha.com/ext-js/4-1/#!/api/Ext.Loader-method-loadScript
For all you googlers out there, I ended up rolling my own by borrowing some Ext code:
var injectScriptElement = function(id, url, onLoad, onError, scope) {
var script = document.createElement('script'),
documentHead = typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
cleanupScriptElement = function(script) {
script.id = id;
script.onload = null;
script.onreadystatechange = null;
script.onerror = null;
return this;
},
onLoadFn = function() {
cleanupScriptElement(script);
onLoad.call(scope);
},
onErrorFn = function() {
cleanupScriptElement(script);
onError.call(scope);
};
// if the script is already loaded, don't load it again
if (document.getElementById(id) !== null) {
onLoadFn();
return;
}
script.type = 'text/javascript';
script.src = url;
script.onload = onLoadFn;
script.onerror = onErrorFn;
script.onreadystatechange = function() {
if (this.readyState === 'loaded' || this.readyState === 'complete') {
onLoadFn();
}
};
documentHead.appendChild(script);
return script;
}
var error = function() {
console.log('error occurred');
}
var init = function() {
console.log('should not get run till the script is fully loaded');
}
injectScriptElement('myScriptElem', 'http://www.example.com/script.js', init, error, this);
From looking at the source it seems to me that you could do it in a bit of a hackish way. Try using Ext.Loader.setPath() to map a bogus namespace to your third party javascript files, and then use Ext.Loader.require() to try to load them. It doesn't look like ExtJS actually checks if required class is defined in the file included.