Skip to main content

Component Lifecycle

This guide introduces the concept of state and the component lifecycle.

Declaring state

The first step to making any component dynamic is to declare a state. Each property declared in state is reactive; when changed, the component will update and reflect the new value. To declare state, add entries to the state option. Each key represents the name of the property and the value must be a configuration object.

Once a state property has been declared, it will be accessible throughout the component options via this.$state. Additionally, an entry for it can be added to the hooks option.

defineComponent({
state: {
stateName: {
type: String, // specifies expected type for runtime type checking
default: '...', // specifies a default value
required: true, // defines if the property is required
readonly: true, // defines if the property is readonly
},
},
});


Lifecycle hooks

The lifecycle hooks are reserved properties that belong to the hooks option. As the name implies, these functions hook into the component lifecycle and are called at a specific time during that lifecycle.

Each of these methods have a designated role and can be used to perform various tasks:

  • $setup: Called once when the component initializes. Useful for setup tasks, like constructing the initial markup, setting the initial state, and/or adding custom listeners.
  • $teardown: Called once when the component is destroyed. Useful for cleanup tasks, like deconstructing the markup, reverting the state, and/or removing custom listeners.
defineComponent({
hooks: {
$setup() {
// setup tasks ...
},
$teardown() {
// teardown tasks ...
},
},
});


State hooks

Once state properties have been declared in state, an entry for them can be added to the hooks option. The entry must have the exact same name as the state property and must be a function. As the name implies, these functions hook into the component state and will be called whenever the value of that state changes. The current and previous value are passed as the first and second argument respectively.

defineComponent({
state: {
flavor: {
default: 'Vanilla',
},
},
hooks: {
flavor(value) {
if (value === 'Vanilla') {
// "flavor" is "Vanilla", do something ...
} else {
// "flavor" is not "Vanilla", do something else ...
}
},
},
});


Setting the state

Setting any state property will trigger an update of the component and all relevant methods and hooks will be called to reflect the new value. While this behavior is universal, the method for setting the state varies between the component definition and the component instance.

info

The strict equality operator (e.g. ===) is used to determine changed values. This means that objects and arrays must be replaced in order for their changes to be recognized.

To set the state internally, via the component definition, apply new values directly to individual properties on this.$state from within the events and/or hooks options.

const FrozenYogurt = defineComponent({
state: {
flavor: {
default: 'Vanilla',
},
},
nodes: {
button: {
type: 'element',
tagName: 'button',
},
},
events: {
button() {
return {
click: () => {
this.$state.flavor = 'Chocolate'; // <-- sets the "flavor" state
},
};
},
},
});

To set the state externally, via the component instance, use the setState class method.

const instance = new FrozenYogurt('#root');

instance.setState({ flavor: 'Chocolate' }); // <-- sets the "flavor" state


Determining the initial state

When a component is initialized, the initial state is collected from multiple sources, merged, and applied to this.$state. In reverse order of priority, the data is collected from the following three sources:

1. The default value from the class definition.

defineComponent({
state: {
flavor: {
default: 'Vanilla',
},
},
});

2. The data-state HTML attribute on the root element. See "HTML-only Usage" to learn more about this feature.

<div data-state='{"flavor": "vanilla"}'></div>

3. The state object passed to the second argument of the constructor.

new FrozenYogurt('#root', { flavor: 'Vanilla' });


Setting the initial state

If needed, state can by set directly within the $setup hook. This is uncommon, but it can be useful for setting dynamic state properties. Setting the state from $setup will not trigger a component update and hooks will not be called. However, relevant component instances declared in the components option will be updated. Keep in mind that setting the state this way will replace the value of any previously determined state and it will ultimately determine the initial state of the component.

defineComponent({
state: {
large: {
default: false,
},
},
hooks: {
setup() {
this.$state.large = window.innerWidth > 500,
},
},
});