In this example, is bookCount memoized the same way as a computed ref is?
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
],
get bookCount() {
return this.books.length
}
})
No, it is not. The getter will be executed everytime the property is accessed.
You can check it by using the getter in the template, and provoke a template re-render. You'll see the getter will be executed at every render, while a regular computed won't (as long as the value didn't change).
Check out this example on sfc.vuejs.org
Related
I'm new to Vue.
I see people suggest that we need to use a Vuex module for components to store their state data so we can access that data in the parent component.
For example, I have MarkdownEditor.vue component and I'll need to access its data from the parent. I store the state in a Vuex module, e.g markdownEditorStore.js... I can access this state in the parent component easily, like, this.$store.state.markdownEditor.XYZ.
That's OK. But what if I have n number of MarkdownEditor components and need isolated states for each of them? This is very basic problem, but how can I handle this?
I need a solution based on instances of module, not a module based one.
The module feature is to split the logic between different domains(Editor in your case is one module). Not for the instances themselves.
The rule of thumb is that the parent that will connect with the Vuex store passes down to MarkdownEditor.vue the data necessary to load. Ideally, MarkdownEditor is able to load just with the props received from the parent. This is a good practice of splitting the visual from the state. Making it easier to test and a clear component API.
Even though you have N possible ME(MarkdownEditor) instances, either you show one at once or multiple. For both cases, you can have a MarkdownEditorDataStore that will hold all the data needed.
Then you just need to access the correct piece of data for each ME instance. And that's up to your store and components structure. Two ways I can think of is that either you have an array for the N ME instance like editors: [ { id: 1, title: X}, { id: 2, title: Y } ] or an object that holds all the data { editors: { 1: {id: 1, title: X }, 2: { id: 2, title: Y } }.
Either you get the data via this.$store.state.MarkdownEditorDataStore.editors[myID]
or
this.$store.state.MarkdownEditorDataStore.editors.find(id => myID === id)
You can have a data property holding the currentEditorId in case you show just one instance at a time. Even better to use a getter in that case to show the actualCurrentEditorObject.
What i understand from your question is that you want a individual state for each component which you can achieve by creating a separate module with namespace=true
for each component
https://vuex.vuejs.org/guide/modules.html#namespacing
I've seen a pattern of using props in of CompositionAPI very often,
that is use toRefs to make all entries of props ref.
I'm kind of confused by it.
For exmaple, from the Vue 3 official guide:
export default {
props: {
user: {
type: String,
required: true
}
},
setup(props) {
const { user } = toRefs(props)
//... use user's value
}
}
I have 2 questions in 2 scenearios:
when the props.user is already reactive
Its value will change if it's changed in any ancestors, so why we need to use toRefs? Doesn't it already reactive?
if it's not reactive, it's just a primitive string value
Does making it reactive means we're going to change its value?
I think making a object reactive also imply that its value is welcomed to be changed.
But all the guides and linters warn us that we'd better not to change the props value.(for not writing to the computed value or something)
If I can change the props value directly in the component, I no longer need to emit the changes to parent component everytime.
It's very convenient but I don't know whenther it is a good idea to change the props value after we're sure it becomes reactive?
Since props aren't supposed to be mutated, this is useful for a specific case that is explained in the documentation; a ref that is computed from a prop needs to be passed as an argument:
const { user } = toRefs(props)
// or
// const user = computed(() => props.user)
someFunction(user);
Where a function makes use of composition API or just needs an argument to be passed by reference rather than by value due to the way it works, e.g.:
function someFunction(val) {
setTimeout(() => {
console.log('Up-to-date value:', unref(val));
}, 1000);
}
I am seeing some weird behaviour here that was unexpected, but it makes intuitive-sense to me in terms of pure JavaScript.
I have a form controller that accumulates a this.thing object that is sent to the server on the final submit. It's a multi-step form, so each step adds some data to this.thing.
So the controller has:
data() {
return {
thing: {},
};
},
The DOM markup for this controller has a child like:
<a-child
:initial-thing="thing"
></a-child>
The child uses that prop to display its initial state, so it receives the prop and sets it into its own local state as instance data:
initialThing: {
type: Object,
required: true,
},
...
data() {
return {
thing: this.initialThing,
};
},
Then this child has a checkbox that is like this:
<a-checkbox
v-model="thing.field"
:initial-value="initialThing.field"
></a-checkbox>
This all works fine, except I just noticed that when the checkbox changes, it's mutating the parent controllers thing.field value.
I'm making this question because I don't understand how Vue can do that, and the only thing that makes sense to me is that when the child does thing: this.initialThing, it's allowing the child to call the setter function on that field on this.initialThing.
It stops mutating the parent's state if I do this instead:
data() {
return {
thing: { ...this.initialThing },
};
},
In my actual app, it's more complex because there are 2 intermediate components, so the grandchild is mutating the grandparent's state, and it stems from the pattern I am describing here.
Can anyone provide a kind of textbook answer for what is happening here? I'm hesitant to rely on this behaviour because the code driving it is not explicit. It makes some of my $emit() events redundant in favour of using this indirect/non-explicit way of sending data upstream.
Also to be clear, this has nothing to do with v-model because it also does it if I do this.thing.field = 'new value';. I believe it has everything to do with inheriting the getters/setters on this.initialThing. Is it safe to rely on this behaviour? If I rely on it, it will make my code more concise, but a naive individual may have a hard time understanding how data is making it into the grandparent component.
This is a shallow copy so you can't prevent mutating grandchildren.
data() {
return {
thing: { ...this.initialThing },
};
},
The solution is below:
data() {
return {
thing: JSON.parse(JSON.stringify(this.initialThing)),
};
},
const initialThing = {
age: 23,
name: {
first: "David",
last: "Collins",
}
}
const shallowCopy = { ...initialThing };
shallowCopy.age = 10;
shallowCopy.name.first = "Antonio"; // will mutate initialThing
console.log("init:", initialThing);
console.log("shallow:", shallowCopy);
const deepCopy = JSON.parse(JSON.stringify(initialThing));
deepCopy.age = 30;
shallowCopy.first = "Nicholas"; // will not mutate initialThing
console.log("------Deep Copy------");
console.log("init:", initialThing);
console.log("deep:", deepCopy);
How it works:
JSON.stringify(this.initialThing)
This converts JSON Object into String type. That means it will never mutate children anymore.
Then JSON.parse will convert String into Object type.
But, using stringify and parse will be expensive in performance. :D
UPDATED:
If you are using lodash or it is okay to add external library, you can use _.cloneDeep.
_.cloneDeep(value); // deep clone
_.clone(value); // shallow clone
I am new to Vue and after checking the docs I can not figure out how to achieve the following:
pass an arbitrarily named variable as a prop to a component instance.
From my understanding, props are meant to be a way to allow data to be passed to a component and as it states on the website:
Passing Data to Child Components with Props:
Props are custom attributes you can register on a component. When a value is passed to a prop attribute, it becomes a property on that component instance.
Since props can be required, it would seem that we can design components under the assumption that some data would be there, and possible within certain parameters (if the validator option is specified).
So I would like to define a function or object outside of vue, e.g. in an application, and pass this function or object to my vue instance.
This works if my named object of function has the exact same name as the prop to which I attempt to bind it. However, as I might have multiple instances of the Vue component and I might want to bind different data, I find using the same name for the variable less than ideal.
Now if I do as the Vue warning suggests, and name object / function the same as the prop, then the warning switches to that my data is not defined inside vue and to make sure it is reactive by reading: https://v2.vuejs.org/v2/guide/components-props.html
which, to be honest, doesnt really explain how to solve the issue,
or move the prop to the data level.
Which I can do (still gives the same warning), but kind of defeats the purpose of having props with my understanding of Vue.
This become more frustrating with anonymous vue instances.
e.g.
<script>
export default {
props: {
// records: {
// default: function(){return{}},
// type: Object
// }
},
data: function() {
return {
records: {} // define even an empty value in data for it to be 'reactive'
}
},
computed: {
fields: function() {
},
keys: function() {
return Object.keys(this.records)
}
},
methods: {
}
}
</script>
trying to use this as a component and set records to var myRecords = {"a": {}} fails:
<my-comp :records="myRecords"/>
So how exactly should I circumvent this? Where should I define my data then? and how should I handle the naming in the case of multiple instances?
A more fledged on example is found on a similar question:
Vue2: passing function as prop triggers warning that prop is already set
So I would like to define a function or object outside of vue, e.g. in an application, and pass this function or object to my vue instance.
It's hard to give a definitive answer because I don't know the specifics of how you have organized your code. Are you using Webpack? Single file components (.vue)? If yes to any of these, then you needn't use global variables in the way you have described in your question.
Your entire Vue app should consist of a single root Vue instance (which you instantiate with new Vue(...), and from there each component is rendered within the root component's template, and templates of those components, and so on.
Looking at the following template:
<my-comp :records="myRecords"/>
myRecords must be a property on the Vue component instance whose template contains the above. It could be declared within the data block, or as a computed property, or a prop, it doesn't matter.
Here's a small example:
<div id="app">
<my-comp :records="myRecords"></my-comp>
</div>
// Obtain records in some way and store it in a global variable
var records = ...
// This is the root Vue instance
new Vue({
el: '#app',
data: {
// You must store the records array in the Vue component like this
// for it to be referenced within the template.
// You can optionally transform the data first if you want.
myRecords: records.filter(r => r.name.startsWith('Bob'))
// ^ ^
// | |
// | +--- the global variable
// |
// +---- the name of the property on the component instance
}
})
Note that MyComp component does not access the records global variable in any way, it only takes its input through the records prop.
I am building a front end for a facial recognition app, and it is my first time using react. I am having a hard time working out how to match the value of one of my state variables to another as a way of identifying what object to modify the state of. Let me lay out the scenario.
I have state variables defined that look like this:
this.state = {current_name: '', correct: 0,
people: [
{name: 'Alice', here: "Not Here"},
{name: 'Bob', here: "Not Here"}]}
The string "current_name" gets updated every half second by making a request to an external endpoint to stream who is in frame. I also have a handler for updating state that looks like this:
CorrectHandler(){
this.setState(prevState =>({
correct: prevState.correct + 1
}));}
which I am triggering with an "onclick" event in my app.
What I would like to be able to do is include in my handler a function to identify a person by the value of their name i.e. "Bob", and change the state of "here" atribute to to a new string.
It seems like there is probably a very simple solution to this but I'm too new to react and javascript to see it. Any advice would be greatly appreciated!
I'd say the best way to do this is using Array.map() and Object.assign() to change your people array into one with new values, like this:
function updateSpecificPerson(person) {
if ( person.name === "Bob" ) {
//it's Bob! Make a shallow copy of him with his new "here" value
return Object.assign({}, person, { here: "somewhere" });
}
// it's not Bob - leave the object alone
return person;
}
this.setState(prevState => {
return {
//copy over all the people, except Bob, who gets modified during copy
people: prevState.people.map(updateSpecificPerson)
};
});
Should be straightforward to change Bob and somewhere to variables you control.