Which lifecycle hook to use on child component to run after parent in Vuejs? - vue.js

On the parent I'm running this:
mounted(){
this.$store.dispatch('fetchNotesAction')
},
components: { ChildComponent }
With that, the Vuex store is populated with data.
I'm assuming, therefore, by the time the child component runs, the store is already full.
In the ChildComponent I'm trying this
mounted(){
console.log(this.$store.getters.getNotes)
},
If I log the exact same code inside the parent, after the dispatch it display the data. So the code (the connection) behind those lines work fine, just am I correct in assuming that I don't need to rerun the fetch (from fetchNotesAction) again?
I tried created instead of mounted and the same thing

The best practice is to use a computed property that returns your state getter then use watch property to observe its changes in the child component :
computed:{
notes(){
return this.$store.getters.getNotes;
}
},
watch:{
notes(newval,oldVal)'
console.log(newval)
}
}

Related

Vue3 child component does not recreating, why?

I have made some sandbox code of my problem here:
https://codesandbox.io/s/clever-zeh-kdff1z
<template>
<div v-if="started">
<HelloWorld :msg="msg" #exit="exit" #remake="remake" />
</div>
<button v-if="!started" #click="started = !started">start</button>
</template>
<script>
import HelloWorldVue from "./components/HelloWorld.vue";
export default {
name: "App",
components: {
HelloWorld: HelloWorldVue,
},
data() {
return {
started: false,
msg: "Hello Vue 3 in CodeSandbox!",
};
},
methods: {
exit() {
this.started = false;
},
remake() {
this.msg = this.msg + 1;
//this code should recreate our child but...
this.exit();
this.started = true;
// setTimeout(() => {
// this.started = true;
// });
},
},
};
</script>
So! We have 2 components parent and child. The idea is simple - we have a flag variable in our parent. We have a v-if statement for this - hide / show an element depend on the flag value "false" or "true". After we toggle the flag - the child component should be recreated. This is the idea. Simple.
In our parent we have a button which will set the flag variable to "true" and our child will be created and will appear on our page.
Ok. Now we have 2 buttons inside our child.
One button is "exit" which is emit an event so the flag variable of parent will set to "false" and the elemint will disappear from our page(It will be destroyed btw). Works as charm. Ok.
The second button "remake". It emit event so the flag variable will be just toggled (off then on). Simple. We set to "false", we set to "true". So the current child should dissapear, and then imediatly will be created new one.
But here we are facing the problem! Ok, current child is still here, there is no any recreation, it just updates current one... So in child I have checked our lifecycle hooks - created and unmounted via console.log function. And the second button dont trigger them. Start->Exit->Start != Start->Remake.
So can anyone please explain me why this is happening? I cant figure it out.
Interesting thing, if you can see there is some asynchronous code commented in my demo. If we set our flag to "true" inside the async function the child will be recreated and we will see the created hook message but it seems like crutch. We also can add a :key to our component and update it to force rerender, but it also seems like a crutch.
Any explanations on this topic how things work would be nice.
Vue re-uses elements and components whenever it can. It will also only rerender once per tick. The length of a 'tick' is not something you should worry yourself about too much, other than that it exists. In your case the this.exit() and this.started = true statements are executed within the same tick. The data stored in this.started is both true in the last tick and the current tick as it does not end the tick in between the statements, and so nothing happens to your component.
In general you should think in states in Vue rather than in lifecycles. Or in other words: What are the different situations this component must be able to handle and how do you switch between those states. Rather than determining what to do in which point in time. Using :key="keyName" is indeed generally a crutch, as is using import { nextTick } from 'vue'; and using that to get some cadence of states to happen, as is using a setTimeout to get some code to execute after the current tick. The nasty part of setTimeout is also that it can execute code on a component that is already destroyed. It can sometimes help with animations though.
In my experience when people try to use lifecycle hooks they would rather have something happen when one of the props change. For example when a prop id on the child component changes you want to load data from the api to populate some fields. To get this to work use an immediate watcher instead:
watch: {
id: {
handler(newId, oldId) {
this.populateFromApi(newId);
},
immediate: true
}
}
Now it will call the watcher on component creation, and call it afterwards when you pass a different id. It will also help you gracefully handle cases where the component is created with a undefined or null value in one of the props you expect. Instead of throwing an error you just render nothing until the prop is valid.

VueJS - Can't assign value from mapState to data property after reloading the page

Can't assign the value from mapState to data property after reloading the page, it works if you go to the child page but not if you are already standing in the child page and reloading the browser.
Computed mapState
computed: {
...mapState({
tsStore: state => state.SchemeStore
})
}
Data Property
data () {
return {
works: '',
offTime: '',
}
}
Mounted
if (this.tsStore.singleView) {
// Set data based on api.
let single = this.tsStore.singleView
this.works = single.works
this.offTime = single.offTime
}
After reloading works and offTime get empty in the data property.
Yes, the problem is the state being updated after the component was mounted;
So, the updated method is called instead of mounted.
It is visible in this fiddle, where the API call is simulated by the setTimeout:
https://jsfiddle.net/eywraw8t/369915/
I think the best way to get the component updated is using computed properties, where Vue implements proxies to watch for changes, like this fiddle:
https://jsfiddle.net/3mn2xgvr/
I moved the changes to a computed properties so when the state in Vuex changes, all data that depends on that changes.

Vue.js - How to pass non-reactive data to child components

I know we can pass data to child components via Props. But it is reactive in one-way data flow mode. If the value of the data is changed in Parent component, it also has effect (update) on props in Child component.
In my case, I don't want to get update on specific prop in Child component even if that data in the Parent component is changed. It is because Child component will only responsible to show the data. But the data in the Parent Component still has to be reactive in Parent Scope.
I've read some forum article that suggest to use like Json. I feel it is a little dirty way and the data in my case is just one string.
Is there anyways to achieve that kind of solution?
You could copy the reactive prop in the created hook of the child component. The copy would not be reactive e.g.
export default {
props: {
reactive: Object
},
data: () => ({
nonreactive: null
}),
created() {
this.nonreactive = Object.assign({}, this.reactive)
}
}
Note: the way you copy the reactive prop will depend on the data type, the way I've shown will work for objects.
Maybe u can check this one
VueJS render once into an element
use v-once on your child component
Use prop as data property in child component. please see the fiddle link:
link here
Vue.component('greeting', {
props: ['user'],
data:function(){
return {
newuser:this.user
}
},
template: '<h1>hi {{ newuser }}</h1>'
});

How to watch child properties changes from parent component

I am using a date picker component library and i want to watch when a property of that component changes.
I have tried this:
watch: {
'$refs.picker.popupVisible': {
handler (new_value) {
console.log('executed')
},
deep: true
}
}
Also this:
computed: {
picker () {
console.log(this.$refs.picker.popupVisible)
return this.$refs.picker.popupVisible
}
}
I know that the solution will be a vue.js hack because this is not the right way.If i had access to child component i would emit en event to parent but unfortunately i don't have.
I had a similar problem using a library which had some limitations.
Unfortunately, your watcher will not work.You have to use the function watcher to make this to work.And you have to use it inside the mounted hook.
mounted() {
this.$watch(
"$refs.picker.popupVisible",
(new_value, old_value) => {
//execute your code here
}
);
}
I also have an example. Please take a look here
What you can do is create a data object in parent component and include the date field in that data object and pass that data object to child component as props
<child :dateObj="dateObj"></child>
data: {
dateObj: {
date: ""
}
}
And in child component you can use the date field of that dateObj props. This is possible because Vue doesn't watch the property of Objects passed as props and we can modify them without Vue complaining in console.
Thus the changed date field is reflected in parent as well.

VueJS access child component's data from parent

I'm using the vue-cli scaffold for webpack
My Vue component structure/heirarchy currently looks like the following:
App
PDF Template
Background
Dynamic Template Image
Static Template Image
Markdown
At the app level, I want a vuejs component method that can aggregate all of the child component's data into a single JSON object that can be sent off to the server.
Is there a way to access child component's data? Specifically, multiple layers deep?
If not, what is the best practice for passing down oberservable data/parameters, so that when it's modified by child components I have access to the new values? I'm trying to avoid hard dependencies between components, so as of right now, the only thing passed using component attributes are initialization values.
UPDATE:
Solid answers. Resources I found helpful after reviewing both answers:
Vuex and when to use it
Vuex alternative solution for smaller apps
In my child component, there are no buttons to emit changed data. It's a form with somewhat 5~10 inputs. the data will be submitted once you click the process button in another component. so, I can't emit every property when it's changing.
So, what I did,
In my parent component, I can access child's data from "ref"
e.g
<markdown ref="markdowndetails"></markdown>
<app-button #submit="process"></app-button>
// js
methods:{
process: function(){
// items is defined object inside data()
var markdowns = this.$refs.markdowndetails.items
}
}
Note: If you do this all over the application I suggest move to vuex instead.
For this kind of structure It's good to have some kind of Store.
VueJS provide solution for that, and It's called Vuex.If you are not ready to go with Vuex, you can create your own simple store.
Let's try with this
MarkdownStore.js
export default {
data: {
items: []
},
// Methods that you need, for e.g fetching data from server etc.
fetchData() {
// fetch logic
}
}
And now you can use those data everywhere, with importing this Store file
HomeView.vue
import MarkdownStore from '../stores/MarkdownStore'
export default {
data() {
sharedItems: MarkdownStore.data
},
created() {
MarkdownStore.fetchData()
}
}
So that's the basic flow that you could use, If you dont' want to go with Vuex.
what is the best practice for passing down oberservable data/parameters, so that when it's modified by child components I have access to the new values?
The flow of props is one way down, a child should never modify its props directly.
For a complex application, vuex is the solution, but for a simple case vuex is an overkill. Just like what #Belmin said, you can even use a plain JavaScript object for that, thanks to the reactivity system.
Another solution is using events. Vue has already implemented the EventEmitter interface, a child can use this.$emit('eventName', data) to communicate with its parent.
The parent will listen on the event like this: (#update is the shorthand of v-on:update)
<child :value="value" #update="onChildUpdate" />
and update the data in the event handler:
methods: {
onChildUpdate (newValue) {
this.value = newValue
}
}
Here is a simple example of custom events in Vue:
http://codepen.io/CodinCat/pen/ZBELjm?editors=1010
This is just parent-child communication, if a component needs to talk to its siblings, then you will need a global event bus, in Vue.js, you can just use an empty Vue instance:
const bus = new Vue()
// In component A
bus.$on('somethingUpdated', data => { ... })
// In component B
bus.$emit('somethingUpdated', newData)
you can meke ref to child component and use it as this
this.$refs.refComponentName.$data
parent-component
<template>
<section>
<childComponent ref="nameOfRef" />
</section>
</template>
methods: {
save() {
let Data = this.$refs.nameOfRef.$data;
}
},
In my case I have a registration form that I've broken down into components.
As suggested above I used $refs, In my parent I have for example:
In Template:
<Personal ref="personal" />
Script - Parent Component
export default {
components: {
Personal,
Employment
},
data() {
return {
personal: null,
education: null
}
},
mounted: function(){
this.personal = this.$refs.personal.model
this.education = this.$refs.education.model
}
}
This works well as the data is reactive.