Vue.js: Why Components’ Data Properties Must Be Functions

Published on May 2, 2017 by

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.

Featured

Learn Vue.js today!

Take an online course and become an Vue.js champion!

Here is what you will learn:

  • How to build advanced Vue.js applications (including SPA)
  • How Vue.js works under the hood
  • Communicating with services through HTTP
  • Managing state of large applications with Vuex
  • ... and much more!
Vue.js logo
Author avatar
Bo Andersen

About the Author

I am a back-end web developer with a passion for open source technologies. I have been a PHP developer for many years, and also have experience with Java and Spring Framework. I currently work full time as a lead developer. Apart from that, I also spend time on making online courses, so be sure to check those out!

Leave a Reply

Your e-mail address will not be published.