I am using the useLazyFetch() composable to fetch server route data from Firebase. I have a problem where when navigating to the page where the data is being fetched, I get an error:
runtime-core.esm-bundler.js:38
[Vue warn]: Unhandled error during execution of mounted hook
at <Index onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< Proxy {__v_skip: true} > key="/post/sed-ut-ultrices-urna-ac-molestie-nulla_jqF8" >
...SNIP...
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'data')
at index.vue:162:1
at callWithErrorHandling (runtime-core.esm-bundler.js:155:22)
at callWithAsyncErrorHandling (runtime-core.esm-bundler.js:164:21)
at hook.__weh.hook.__weh (runtime-core.esm-bundler.js:2687:29)
at flushPostFlushCbs (runtime-core.esm-bundler.js:358:32)
at flushJobs (runtime-core.esm-bundler.js:403:9)
Here is my script setup file:
watch(post, (newVal) => {
if (newVal) {
// <----- What to write here???
}
})
onMounted(() => {
if (userStore.userProfile) {
// watch the bookmark
const { firestore } = useFirebase()
const docRef = doc(
firestore,
`users/${userStore.userProfile.uid}/bookmarks`,
post.value.data.id // <--- this seems to be the problem as data is `null`
)
unsubscribe.value = onSnapshot(docRef, {
next: (doc) => {
if (doc.exists()) {
watchedBookmark.value = [doc.data().id]
loading.value = false
} else {
watchedBookmark.value = []
loading.value = false
}
},
error: (err) => {
console.error('watchedBookmark listener error', err)
}
})
}
loading.value = false
})
According to the docs, it looks like I may need to:
the async function does not block navigation. That means you will need
to handle the situation where the data is null
` https://v3.nuxtjs.org/guide/features/data-fetching#uselazyfetch
OK, so how exactly would I do that when doing the data fetch like so?
const { pending, data: post } = useLazyFetch(`/api/posts/${route.params.id}`, {
initialCache: false
})
Related
I'm using a composable provided by Vuejs.org that looks like this
// fetch.js
import { ref, isRef, unref, watchEffect } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
function doFetch() {
// reset state before fetching..
data.value = null
error.value = null
// unref() unwraps potential refs
fetch(unref(url))
.then((res) => res.json())
.then((json) => (data.value = json))
.catch((err) => (error.value = err))
}
if (isRef(url)) {
// setup reactive re-fetch if input URL is a ref
watchEffect(doFetch)
} else {
// otherwise, just fetch once
// and avoid the overhead of a watcher
doFetch()
}
return { data, error }
}
And within the script tag I'm using
let loopableValues = ref([])
const { data, error } = useFetch(
'https://jsonplaceholder.typicode.com/todos/',
)
loopableValues.value = data
return { loopableValues }
My issue is that the variable "data" is not a pure array that I can loop using something like this in the template:
<div v-for="value in loopableValues" :key="value.id"> {{ value.id}}</div>
Cause when I run this I get "Uncaught (in promise) TypeError: value is undefined". So I think I need to handle the promise somehow in order to loop out the data as above, but I'm stuck and don't know how to go forward. Do I need to use a function on the data coming back from the fetch composable or is it something else I'm misssing?
I am trying to create a plugin for rxdb.
I want to catch the exception raised by insert and return an hash with
{[fieldName: string] => [error:string]}
When using my new method though, I am getting an exception, and it seems like the method is getting called directly on the prototype rather than on each RxColletion<T, T2, T3> instance.
The error i am getting is:
TypeError: Cannot read property 'fillObjectWithDefaults' of undefined
which happens here: https://github.com/pubkey/rxdb/blob/ac9fc95b0eda276110f371afca985f949275c3f1/src/rx-collection.ts#L443
because this.schema is undefined.. The collection I am running this method on does have a schema though..
Here is my plugin code:
export const validatedInsertPlugin: RxPlugin = {
rxdb: true,
prototypes: {
RxCollection(proto: IRxCollectionBaseWithValidatedInsert) {
proto.validatedInsert = async function validatedInsert<T, D>(
doc: T
): Promise<Insert<T>> {
try {
// this is the line that raises:
const product = await proto.insert(doc);
return [true, product];
} catch (e) {
// extract errors
return [false, {} as Errors<T>];
}
};
},
},
overwritable: {},
hooks: {},
};
To answer my own question,
proto.insert is targeting the prototype, which is not what I want.
function(this: RxCollection) is what I want. I have to use this which will target the actual instance.
proto.validatedInsert = async function validatedInsert<T1>(
this: RxCollection,
doc: T1
): Promise<ValidatedInsert<T1>> {
try {
const product = await this.insert(doc); // this, not proto
return [true, product];
} catch (e) {
...
I'm new to this and there are several similar questions such as redux-observable - dispatch multiple redux actions in a single epic but I can't see how they apply to my usecase.
I'm using a Subject to emit multiple events based on processing a file and uploading to a server
export function UploadSceneWithFile(scene){
const subject$ = new Subject()
FileToScenePreview(scene,scene.file).then(res => subject$.next(res))
const uploader = new S3Upload({
....
onError:()=>subject$.next('error'),
onProgress: (val)=> subject$.next(val),
onFinishS3Put: ()=>subject$.next('complete'),
})
uploader.uploadFile(scene.file)
return subject$
}
A want to write an epic that captures these events and dispatches actions based on the data coming back.
ie. something like this
export function uploadSceneFile(action$) {
return action$.ofType(CREATE_SCENE_SUCCESS)
.mergeMap(({payload}) =>
UploadSceneWithFile(payload)
.subscribe(res => {
console.log(res)
if (!res.thumbName) {
return { type: UPLOAD_SCENE_FAILED, message: 'failed' }
} else {
return {type: UPLOAD_SCENE_SUCCESS, payload: res }
}
if (!res.value) {
return { type: UPLOAD_SCENE_THUMB_FAILED, message: 'failed' }
} else {
return {type: UPLOAD_SCENE_THUMB_SUCCESS, payload: res }
}
})
)
}
I'm getting this error:
TypeError: You provided an invalid object where a stream was expected.
You can provide an Observable, Promise, Array, or Iterable.
i can console log the results OK, but I'm not dispatching any actions.. any ideas how I go about this?
What's happening is that you're returning a subscription inside your mergeMap, instead of an Observable that it expects. Instead of using subscribe it looks like you want to use map
export function uploadSceneFile(action$) {
return action$.ofType(CREATE_SCENE_SUCCESS)
.mergeMap(({payload}) =>
UploadSceneWithFile(payload)
.map(res => { // <------------------ map, not subscribe
console.log(res)
if (!res.thumbName) {
return { type: UPLOAD_SCENE_FAILED, message: 'failed' }
} else {
return {type: UPLOAD_SCENE_SUCCESS, payload: res }
}
if (!res.value) {
return { type: UPLOAD_SCENE_THUMB_FAILED, message: 'failed' }
} else {
return {type: UPLOAD_SCENE_THUMB_SUCCESS, payload: res }
}
})
)
}
In redux-observable you will almost never call subscribe yourself, instead composing Observables. The only time you'd use it is mostly with custom operators that aren't super common.
I'm dispatching an action when a form is submitted, so then checkPasswd is reached:
public checkPasswd():void {
this.store$.dispatch({ type: 'USER_REDUCER_USER_LOGIN', payload: { username: this.form.value.mail, password: this.form.value.passwd } });
}
on ngOnInit - constructor I've created this subscription to Store<IStore>:
private user$: Observable<IUser>;
private userSub: Subscription;
constructor(private store$: Store<IStore>)
{
this.user$ = this.store$.select(state => state.user).filter(user => user.id != null);
}
ngOnInit():void {
this.userSub = this.user$.subscribe(
(user: IUser) => {
this.router.navigate(['/app']); (((2)))
},
(error: any) => {
this.addAlert(error.message); (((1)))
}
);
}
I'm checking that (((1))) is reached when an error occurs. Nevertheless, it's never reached when an error appears.
I'm using efffects:
#Effect({ dispatch: true })
userLogin$: Observable<Action> = this._actions$
.ofType('USER_REDUCER_USER_LOGIN')
.switchMap((action: Action) =>
this._userService.checkPasswd(action.payload.username, action.payload.password)
.map((user: any) => {
return { type: 'USER_REDUCER_USER_LOGIN_SUCCESS', payload: user };
})
.catch((err) => {
return Observable.of({ type: 'USER_REDUCER_USER_LOGIN_FAILED', payload: { error: err } });
})
);
As you can see, in my effect I'm trying to call to my service, and if it gets an error I'm returning Observable.of({ type: 'USER_REDUCER_USER_LOGIN_FAILED', payload: { error: err } });.
So, after that, this reducer is called and reached:
public static USER_LOGIN_FAILED = `${UserReducer.reducerName}_USER_LOGIN_FAILED`;
private static userLoginFailed(sourcesRdx, type, payload) {
return Object.assign(<IUser>{}, sourcesRdx, payload);
}
Nevertheless, (((1))) is not reached, neither (((2))). So, the call stack seems to not reach my subscription...
Any ideas?
First: your select filters out any emission that do not contain a user-id, which I assume would be the case when a login fails - this means that absolutely no data will be emitted beyond this point unless there is a valid user with an id in the store.
this.user$ = this.store$.select(state => state.user).filter(user => user.id != null);
Second: The store.select will never emit any errors, since it is a perpetual stream - if errors where emitted, that would break the stream (this is one of the RxJS core-principles: Errors will stop and finalize a stream).
So the behavior seems to be expected to me.
How to handle this? - If you are looking to persist errors in your ngrx-store, then you would have to implement a field for that and add a select to get errors and display them - if you do not want to go through the store, you could add an error-alert-handler to the effect directly.
Also have a look at the ngrx-example-app for best-practices: https://github.com/ngrx/example-app
I have a simple Meteor subscription, and I display a loading message while the data is being loaded. But I don't know how to display error message if subscription failed.
export const MyAwesomeComponent = createContainer(() => {
let sub = Meteor.subscribe('some-data');
if (!sub.ready()) return { message: 'Loading...'};
if (sub.failed()) return { message: 'Failed.' }; // How to do this?
return {
data: Data.find().fetch()
}
}, MyInternalRenderComponent);
Problem is, the subscription object doesn't have a failed() method, only a ready() query. How to pass the failure of a subscription as props in a createContainer() method?
I know the Meteor.subscribe method has an onStop callback for this case, but I don't know how to glue it toghether that to pass a property.
After a lot of researching I managed to get this working and I think it answers your question.
Bear in mind I'm using Meteor 1.6, but it should give you the info to get it working on your side.
On the publication/publish:
try {
// get the data and add it to the publication
...
self.ready();
} catch (exception) {
logger.error(exception);
// send the exception to the client through the publication
this.error(new Meteor.Error('500', 'Error getting data from API', exception));
}
On the UI Component:
const errorFromApi = new ReactiveVar();
export default withTracker(({ match }) => {
const companyId = match.params._id;
let subscription;
if (!errorFromApi.get()) {
subscription = Meteor.subscribe('company.view', companyId, {
onStop: function (e) {
errorFromApi.set(e);
}
});
} else {
subscription = {
ready: () => {
return false;
}
};
}
return {
loading: !subscription.ready(),
company: Companies.findOne(companyId),
error: errorFromApi.get()
};
})(CompanyView);
From here all you need to do is get the error prop and render the component as desired.
This is the structure of the error prop (received on the onStop callback from subscribe):
{
error: String,
reason: String,
details: String
}
[Edit]
The reason there is a conditional around Meteor.subscribe() is to avoid an annoying infinite loop you'd get from the natural withTracker() updates, which would cause new subscriptions / new errors from the publication and so on.