Why Components’ Data Properties Must Be Functions

This entry is part 52 of 55 in the series Vue.js: From Beginner to Professional

I previously mentioned that with components, the data property must be a function and not an object as we are used to. I have opened up an example of a component which contains a button.

<div id="app">
	<counter></counter>
</div>
Vue.component('counter', {
	template: `
		<div>
			<button @click="counter++">{{ counter }}</button>
		</div>
	`
});

new Vue({
	el: '#app'
});

When clicking it, I want to increase a data property, which I haven’t added yet. Now I want to show you why adding an object for the data property won’t work, so I’ll just go ahead and add an object.

Vue.component('counter', {
	template: `
		<div>
			<button @click="counter++">{{ counter }}</button>
		</div>
	`,
	data: {
		counter: 0
	}
});

If we open the browser’s console, we can see that Vue issues a warning that the property must contain a function, and the component doesn’t work either.

Alright, so that clearly didn’t work. Let’s change the property to be a function instead, then.

Vue.component('counter', {
	template: `
		<div>
			<button @click="counter++">{{ counter }}</button>
		</div>
	`,
	data: function() {
		return {
			counter: 0
		};
	}
});

Now the component works. But let’s try to move the object out of the component and into a variable.

var data = {
	counter: 0
};

And then return this object within the data property’s function.

data: function() {
	return data;
}

Let’s run the code again.

Seems to work, doesn’t it? Clicking the button correctly increases the counter.

Let’s see what happens if we add the component to the page a couple of times more.

<div id="app">
	<counter></counter>
	<counter></counter>
	<counter></counter>
</div>

Let’s see what happens if I click one of the buttons now.

Notice how all of the numbers on the buttons changed. This is because all three instances of the component share the same data object, because when Vue invokes the function to retrieve the object, I return a reference to the same data object each time. This is what Vue tries to prevent by having us return an object within a function. In this case I just worked my way around it just to show you the problem.

Let’s now fix it by returning a new object within the function, so that every time it’s called, a fresh object is returned. This will mean that each component instance is self-contained as it should be and will contain its own internal state.

Vue.component('counter', {
	template: `
		<div>
			<button @click="counter++">{{ counter }}</button>
		</div>
	`,
	data: function() {
		return {
			counter: 0
		};
	}
});

Clicking the buttons verifies that it works as intended now.

So requiring that the data property contains a function, is really just a precaution that Vue takes for us to prevent us from messing up and doing bad things where components do not have their own state. And, unless you hack around it, Vue will catch you making the mistake.

So to summarize, the data property should be a function in the context of components, and this function should always return a fresh object.

Series Navigation<< Introduction to ComponentsGlobal & Local Components >>

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *