StencilJSでPropは必ずしも再レンダリングしないのでWatchを併用しよう
Stencilでとても簡単なコンポーネントを用意したとします。 label
属性をもったボタンコンポーネントです。
@Component({
tag: 'my-button',
styleUrl: './my-button.scss',
scoped: true
})
export class ButtonComponent {
@Prop() label: string = '';
render() {
return (
<button>{this.label}</button>
);
}
}
これは <my-button label="button"></my-button>
として利用することができます。初回に表示される時、 @Prop
がある状態でrenderされるので、当然レンダリングされた結果は <button>button</button>
として表示されます。
では、レンダリング後に label
属性をJSでハンドリングして変更するとどうなるでしょうか。
const button = document.querySelector('my-button');
button.setAttribute('label', 'my-button')
このように変更しても my-button
は再レンダリングされません。これは、 ButtonComponent
としてみると this.label
は値が変わっただけであるため、再度 render
が走らないためです。これは常に @Prop
を値レベルで監視していると変更検知が重くなり、また不要なレンダリングを行うことを防ぐためにこうなっています。
まず、変更を検知するために @Watch
デコレーターが用意されています。上記コンポーネントを変更してみましょう。
@Component({
tag: 'my-button',
styleUrl: './my-button.scss',
scoped: true
})
export class ButtonComponent {
@Prop() label: string = '';
+ @Watch('label')
+ async watchLabelHandler() {
+ }
render() {
return (
<button>{this.label}</button>
);
}
}
これで、 label
が変更される度に、 watchLabelHandler()
が実行されるようになりました。しかし、まだ render()
は実行されません。 render()
を実行するためには @State
デコレーターのついたプロパティが変更される必要があるため、再レンダリング監視用に @State _label
プロパティを用意しましょう。
@Component({
tag: 'my-button',
styleUrl: './my-button.scss',
scoped: true
})
export class ButtonComponent {
@Prop() label: string = '';
+ @State() _label: string;
@Watch('label')
async watchLabelHandler() {
+ this._label = this.label;
}
render() {
return (
<button>{this.label}</button>
);
}
}
これで、 label
属性が変更される度に @Watch('label')
で監視しているために watchLabelHandler()
メソッドが実行され、その中で変更があると再レンダリングされる _label
が変更されるために再レンダリングが走るようになりました。
簡単ですね。
それではまた。
Discussion