Vue composition API value set, but still null in catch - vuejs2

I have created this logic that when doing it's happy path, everything works fine. But when there is an error, I want to display it on the interface.
My code looks like this (trimmed):
export function initCamera() {
const loaded = ref(false);
const cameras = ref([]);
const error = ref(null);
someTask().then((response) => {
cameras.value = response;
loaded.value = true;
})
.catch((error) => {
console.log("we got an error", error);
loaded.value = true;
error.value = error;
console.log(error.value);
});
return { loaded, cameras, error };
}
If there are no errors, then the interface does have access to the cameras and the loaded flag is set to true.
If there is an error, it does set the loaded flag, but the error is always null.
All the console logs you see there display a value.
My component looks like this:
export default defineComponent({
name: "TheScanner",
directives: {
content,
},
emits: ["onExpand"],
setup() {
let init = false;
const result = reactive({ loaded: Boolean, cameras: null, error: null });
const expanded = ref(false);
const instance = getCurrentInstance();
const expand = (value: boolean) => {
expanded.value = value;
instance.proxy.$emit("onExpand", value);
};
watch(
() => expanded.value,
(value) => {
if (!value || init) return;
init = true;
Object.assign(result, initCamera());
console.log(result);
}
);
return { expanded, expand, ...toRefs(result) };
},
});
As you can see, I have setup the result as a reactive property and use Object.assign to assign my response to it. That console log there will show the cameras and loaded boolean values, but never shows the error. It's always null.
Does anyone know why?

Change the variable name 'error' into someting else, because you have declared another variable named 'error' above, the ref. In catch block, the variable 'error' points the error object, not the ref.
export function initCamera() {
const loaded = ref(false);
const cameras = ref([]);
const error = ref(null);
someTask().then((response) => {
cameras.value = response;
loaded.value = true;
})
// Pay attention to this line
.catch((err) => {
console.log("we got an error", err);
loaded.value = true;
error.value = err;
console.log(error.value);
});
return { loaded, cameras, error };
}

Related

vue apollo 2 composition api track result

So I am trying to add product impressions to my site by following this article:
https://developers.google.com/tag-manager/enhanced-ecommerce#product-impressions
I have created a bit of logic to fire off the required data like this:
import { getCurrentInstance } from "#vue/composition-api";
import { useGtm } from "#gtm-support/vue2-gtm";
export function useTrackProductImpressions(items: any[]) {
console.log("trying to track products", items);
if (!items?.length) return;
const gtm = useGtm();
if (!gtm.enabled()) return;
const dataLayer = window.dataLayer;
if (!dataLayer) return;
console.log(items);
const products = items.map((product, i) => {
const retailers = product.retailers ?? [];
return {
name: product.title, // Name or ID is required.
id: product.id,
price: retailers[0].price,
brand: product.brand,
category: product.categorySlug,
variant: product.variant,
position: i,
};
});
const instance = getCurrentInstance();
const route = instance.proxy.$route;
const routeName = route.meta?.title ?? route.name;
dataLayer.push({ ecommerce: null }); // Clear the previous ecommerce object.
dataLayer.push({
event: "productClick",
ecommerce: {
click: {
actionField: { list: routeName }, // Optional list property.
products,
},
},
// eventCallback: function () {
// document.location = productObj.url;
// },
});
}
This seems pretty normal and I have a click version of this that works fine.
The problem is, the click event can be fired when a link is clicked, this one needs to fire when the view loads, I assume in setup.
So, I have my apollo logic:
import { useQuery, useResult } from "#vue/apollo-composable";
import * as listProducts from "#graphql/api/query.products.gql";
export const defaultParameters: {
identifier?: string;
searchTerm: string;
itemsToShow: number;
page: number;
filters: any;
facets: string[];
} = {
searchTerm: "*",
itemsToShow: 12,
page: 1,
filters: [],
facets: ["Criteria/Attribute,count:100"],
};
export function useSearchProducts(params) {
const { result, loading, error, fetchMore } = useQuery(listProducts, params);
const response = useResult(result, null, (data) => data.searchProducts);
return { response, loading, error, fetchMore };
}
And from my setup I invoke like this:
const { category } = toRefs(props);
const page = ref(1);
const skip = ref(0);
const orderBy = ref([
{
key: "InVenue",
value: "desc",
},
]);
const params = computed(() => {
const filters = createFilters("CategorySlug", [category.value.slug]);
const request = createRequest(
defaultParameters,
page.value,
filters,
orderBy.value
);
return { search: request };
});
const { response, loading, error} = useSearchProducts(params);
Which I can then return to the template like this:
return { response, loading, error };
Now I have done this, I want to add some tracking, so initially I did this:
watch(response, (result) => useTrackProductImpressions(result?.value?.items));
But it was always undefined.
I added console log on result within the watch method and it is always undefined.
So I changed to this:
const track = computed(() => {
useTrackProductImpressions(response.value.items);
});
But this never gets invoked (I assume because it has no return value and I don't use it in the template).
My question is, which is the best way to do what I am attempting? Am I missing something or am I on the write track?
I think I was close, I just used the computed property to return my products like this:
const products = computed(() => {
if (!response.value) return [];
useTrackProductImpressions(response.value.items);
return response.value.items;
});
const total = computed(() => {
if (!response.value) return 0;
return response.value.total;
});
const hasMoreResults = computed(() => {
if (!response.value) return false;
return response.value.hasMoreResults;
});
return {
products,
loading,
error,
total,
hasMoreResults,
skip,
more,
search,
};

strange console.log output with vuex

i have some simple vuex store with
const state = {
todos : []
}
const getters = {
allTodos: (state) => state.todos
}
const actions = {
async fetchTodos({ commit }) {
console.log(this.state.todos)
if(state.todos.length == 0) {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos?_limit=5')
commit('setTodos', response.data)
}
}
}
const mutations = {
setTodos(state, todos) {
state.todos = todos
}
}
why does console.log in fetchTodos action output populated todos before it was populated with axios.get and setTodos mutation?
when i write
const actions = {
fetchTodos({ commit }) {
console.log(this.state.todos)
setTimeout(async () => {
if(state.todos.length == 0) {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos?_limit=5')
commit('setTodos', response.data)
}
}, 10000)
}
}
output is normal with empty todos in state
That's because you will see a little blue triangle right next to the console log. I don't know the technical term for it but what happens is that the browser will update that variable with the current value because it is a reactive variable and since it is a reference being pointed to a location in memory, it will update.
If you truly wish to see the value and prove what was described above, you can write:
console.log(JSON.parse(JSON.stringify(this.state.todos)));

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 </>;
};

Vue's composition API ref contains data returns null when .value is called

I have a composable that fetches and returns ref documents in real-time from Firestore. The ref contains data but when I call .value in the setup(), null is returned.
The composable is as follows.
import { watchEffect, ref } from "vue";
import { projectFirestore } from "../firebase/config";
const getDocument = (collection, id) => {
let document = ref(null);
let error = ref(null);
let documentRef = projectFirestore.collection(collection).doc(id);
const unsub = documentRef.onSnapshot(
doc => {
if (doc.data()) {
document.value = { ...doc.data(), id: doc.id };
error.value = null;
} else {
error.value = "That document does not exist.";
}
},
err => {
console.log(err.message);
error.value = "Problem fetching the document.";
}
);
watchEffect(onInvalidate => {
onInvalidate(() => unsub());
});
return { error, document };
};
export default getDocument;
When I dump the result of this, console.log(result), I get the output in the capture below but result.value is null.
How can I get the values within setup()?
Please note that the capture is a result of console.log(result) & console.log(result.value).

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 ...