How to access Nuxt context inside of fetch() hook? - javascript

I want to access the props inside the async fetch() but I'm also using async fetch(context). So, I'm not sure how to access the props.

In Nuxt 2, you have 2 fetch hooks.
The old one, before Nuxt 2.12, fetch(context) which acts a lot like asyncData. It's executed before the component creation, so you don't have access to it (data, props, options... nothing).
This one is deprecated, use asyncData instead.
The new one, from Nuxt 2.12, fetch() (with no parameters). It's executed at the same time as the created() hook. It has access to the component's context (props, data, etc.).
fetch(context) {
// "this" doesn't exists
// context is the Vue global context
}
fetch() {
this.myProp // "this" exists and have access to props
}

As shown in the documentation, you can access the context with this when using the fetch() hook like
async fetch() {
await this.$axios // or whatever this.$i18n etc.....
}
Don't use fetch(context) as explained here.

Related

What is the role and usage of the setup function provided by vue3's Composition API

I need to know the correct usage and the best practice of the setup function provided by vue3's Composition API.
I checked in my current project where developers actually use the setup function instead of creating the component with the traditional approach.
If it is just a design principle or improvement something then where we should apply these. I read the official documentation but instead, they didn't explain the concept, they just provided the list of arguments available in this function.
MyBook.vue
<template>
<span>Warning:- {{warning}}</span>
<button #click="warning = !warning">toggle</button>
</template>
<script>
import { ref } from 'vue'
export default {
props: ['warning'],
setup(props, context) {
const warning = ref(props.warning)
return {
warning,
}
},
}
</script>
<MyBook
:warning="true"
/>
As you can see above, I can't use the same name of a property to data attribute for a component but in the case of setup, we can do this and update the value. (as property should not change within component).
The Vue devtool is also showing the setup as a different category.
setup sets up an instance and returns properties that it should have. The purpose of Composition API, which setup is a part of, is to replace Options API, where an instance is determined by component options. So setup is the replacement for data, methods, computed, watch and lifecycle hooks.
As the reference explains, setup also replaces beforeCreate and created lifecycle hooks, the rest of hooks are set inside of it.
There is no conflict between data and props in setup function because props is accessible as setup parameter, i.e. warning and props.warning are accessible at the same time. In a template, they aren't and shouldn't be distinguished, they instance properties, the solution is to not allow name conflicts. They have been previously available with $data.warning and $props.warning magic keywords but their use wasn't encouraged. If warning value differs from a prop of the same name, and both should be available in a template, it should have a different name.

Vue reactive data showing as Observer and not actual value

I have some methods in a component:
created() {
return this.getConversation(this.$route.params.ConversationId).then(
respConversation => {
this.conversation = respConversation;
}
);
},
mounted() {
console.log(this.conversation);
return this.getConversationTranscripts(this.conversation.AudioId);
},
However, this.conversation prints as {__ob__: Observer} and doesn't have AudioId
Both this.getConversation and this.getConversationTranscripts return promises.
Returning Promises from Vue's lifecycle hooks doesn't do much. It certainly doesn't make them wait for the Promise to complete. The same would be true using async/await.
Internally the hooks are called using callHook, e.g. callHook(vm, 'mounted'). You can see the code for callHook at:
https://github.com/vuejs/vue/blob/b7c2d9366cf731a1551286b8ac712e6e0905070e/src/core/instance/lifecycle.js#L336
This calls out to invokeWithErrorHandling, which you can see at:
https://github.com/vuejs/vue/blob/b7c2d9366cf731a1551286b8ac712e6e0905070e/src/core/util/error.js#L36
While invokeWithErrorHandling does have some minimal support for handling any Promises that are returned, those Promises are ignored by callHook.
In the code shown in the question the created hook will be called followed by the mounted hook. The then on the getConversation Promise won't be called until later as that will be asynchronous.
So at the point mounted is called the value of this.conversation will still be its initial value, presumably an empty object. Vue's reactivity system will cause that to be shown as {__ob__: Observer} in the console.
If the component needs that data to be able to function then the parent will have to take responsibility for loading that data and delay creating the component until the data is available.
More likely the component will just need to cope with the data being missing when it's first rendered. It will then need to chain together the asynchronous calls using Promises:
created() {
this.getConversation(this.$route.params.ConversationId).then(
respConversation => {
this.conversation = respConversation;
this.getConversationTranscripts(this.conversation.AudioId);
}
);
}
Using async/await is maybe a little cleaner here:
async created() {
this.conversation = await this.getConversation(this.$route.params.ConversationId)
this.getConversationTranscripts(this.conversation.AudioId);
}
None of this will pause the component's lifecycle, so rendering will continue with this.conversation still set to its initial value and the template will have to be coded to deal with that.
I would add that using this.$route.params.ConversationId is not ideal. If possible this would be injected via a prop instead. You'll also need to be very careful when this value changes. If the value changing causes a new component to be created then no problem but if it just updates the existing component you could run into problems. The created and mounted hooks only run once, when the component is first created, so reusing the same component when the params change won't run them again.

Can you use state or call other methods inside the getInitialProps() method in Next.js?

I'm trying to do this but it's not working:
static async getInitialProps({ req, res, query }) {
// Call another method in the component
const foo = this.foo();
// Use the component's state here
const foobar = [...foo, ...this.state.bar];
}
This is just pseudo code, in my actual use case I'm trying to build a URL to send an AJAX request. This URL is built by a method in my component which is also called by the client-side rendering. So I want to keep it in the same place and use it for both the initial rendering and the subsequent client-side rendering. But I can't call that method this.buildURL() from inside getInitialProps. I also can't use state in that function, how to go about this or a workaround?

Passing parameters to Vuex getters from a Vuex action

I have a Vuex getter that I call from various components in my application. However, I have found a case were slightly more complex logic is required before calling the getter, so I am making use of a Vuex Action. How can I call the getter function with a parameter from my action?
I use constants for naming getters/mutations/actions, so my getter is defined as follows: [GETTER_NAME]: state => param => { return {/.../} }. In my Vuex action, I would like to call the getter as follows getters[GETTER_NAME](someParam). However, this does not seem to work (even though getters[GETTER_NAME] returns a function).
Calling the getter from a component works perfectly fine. I simply create computed function and use ...mapGetters({getterName: GETTER_NAME}). To call the getter with a parameter I just say getterName(someParam).
[GETTER_NAME]: state => param=> {
return {/.../}
},
[ACTION_NAME]: (context, param) => {
getters[GETTER_NAME](param)
? context.commit(MUTATION_X, param)
: context.commit(MUTATION_Y, param);
}
The getter gets called, however, it returns the function without passing in the parameter. Am I doing something wrong or am I misunderstanding the way getters work in Vuex?
You need to call like context.getters[GETTER_NAME](someParam) inside actions here.
[GETTER_NAME]: state => param=> {
return {/.../}
},
[ACTION_NAME]: (context, param) => {
context.getters[GETTER_NAME](param)
? context.commit(MUTATION_X, param)
: context.commit(MUTATION_Y, param);
}
In actions have injected parameters : dispatch, commit, getters and rootState. Therefore you can access getters like this:
ACTION_NAME: ({ commit, getters }, payload) => {
let MY_VARIABLE = getters.GETTER_NAME(payload)
console.log(MY_VARIABLE)
}
This works fine even if you try to access a getter from a different module.
Though you can use getters with context via context.getters the syntax gets a bit longish inside the action when using it this way.

Can I call APIs in componentWillMount in React?

I'm working on react for last 1 year. The convention which we follow is make an API call in componentDidMount, fetch the data and setState after the data has come. This will ensure that the component has mounted and setting state will cause a re-render the component but I want to know why we can't setState in componentWillMount or constructor
The official documentation says that :
componentWillMount() is invoked immediately before mounting occurs. It
is called before render(), therefore setting state in this method will
not trigger a re-rendering. Avoid introducing any side-effects or
subscriptions in this method.
it says setting state in this method will not trigger a re-rendering, which I don't want while making an API call. If I'm able to get the data and able to set in the state (assuming API calls are really fast) in componentWillMount or in constructor and data is present in the first render, why would I want a re-render at all?
and if the API call is slow, then setState will be async and componentWillMount has already returned then I'll be able to setState and a re-render should occur.
As a whole, I'm pretty much confused why we shouldn't make API calls in constructor or componentWillMount. Can somebody really help me understand how react works in such case?
1. componentWillMount and re-rendering
Compare this two componentWillMount methods.
One causes additional re-render, one does not
componentWillMount () {
// This will not cause additional re-render
this.setState({ name: 'Andrej '});
}
componentWillMount () {
fetch('http://whatever/profile').then(() => {
// This in the other hand will cause additional rerender,
// since fetch is async and state is set after request completes.
this.setState({ name: 'Andrej '});
})
}
.
.
.
2. Where to invoke API calls?
componentWillMount () {
// Is triggered on server and on client as well.
// Server won't wait for completion though, nor will be able to trigger re-render
// for client.
fetch('...')
}
componentDidMount () {
// Is triggered on client, but never on server.
// This is a good place to invoke API calls.
fetch('...')
}
If you are rendering on server and your component does need data for rendering, you should fetch (and wait for completion) outside of component and pass data thru props and render component to string afterwards.
ComponentWillMount
Now that the props and state are set, we finally enter the realm of Life Cycle methods
That means React expects state to be available as render function will be called next and code can break if any mentioned state variable is missing which may occur in case of ajax.
Constructor
This is the place where you define.
So Calling an ajax will not update the values of any state as ajax is async and constructor will not wait for response. Ideally, you should use constructor to set default/initial values.
Ideally these functions should be pure function, only depending on parameters. Bringing ajax brings side effect to function.
Yes, functions depend on state and using this.setState can bring you such issues (You have set value in state but value is missing in state in next called function).
This makes code fragile. If your API is really fast, you can pass this value as an argument and in your component, check if this arg is available. If yes, initialise you state with it. If not, set it to default. Also, in success function of ajax, you can check for ref of this component. If it exist, component is rendered and you can call its state using setState or any setter(preferred) function.
Also remember, when you say API calls are really fast, your server and processing may be at optimum speed, but you can never be sure with network.
If you need just data only at first run and if you are ok with that. You can setState synchronously via calling a callback.
for eg:
componentWillMount(){
this.setState({
sessionId: sessionId,
}, () => {
if (this.state.hasMoreItems = true) {
this.loadItems() // do what you like here synchronously
}
});
}

Categories

Resources