Stencil 组件不呈现更新的选项卡
Stencil component not rendering the updated tabs
import { Component, h, State } from '@stencil/core';
// import '@webcomponents/custom-elements';
import '@clr/core/icon/register';
import { ClarityIcons, plusIcon } from '@clr/core/icon';
ClarityIcons.addIcons(plusIcon);
@Component({
tag: 'tabs-component',
styleUrl: 'tabs-component.css',
shadow: false,
})
export class TabsComponent {
@State() tabs: Array<object> = [
(
<li role="presentation" class="nav-item">
<button id="tab3" class="btn btn-link nav-link" aria-controls="panel3"
aria-selected="false" type="button">Cloud</button>
</li>
)
];
addTab(onHead = true) {
// debugger
const tab = (
<li role="presentation" class="nav-item">
<button id="tab3" class="btn btn-link nav-link" aria-controls="panel3"
aria-selected="false" type="button">Dashboard</button>
</li>
);
if (onHead) {
this.tabs.unshift(tab);
} else {
this.tabs.push(tab);
}
console.log(this.tabs);
}
render() {
return (
<div>
<ul id="demoTabs" class="nav" role="tablist">
<li role="presentation" class="nav-item" onClick={() => this.addTab()}>
<cds-icon shape="plus" class="cursor"></cds-icon>
</li>
{this.tabs}
</ul>
<section id="panel1" role="tabpanel" aria-labelledby="tab1">
tab1
</section>
<section id="panel2" role="tabpanel" aria-labelledby="tab2" aria-hidden="true">
tab2
</section>
<section id="panel3" role="tabpanel" aria-labelledby="tab3" aria-hidden="true">
tab3
</section>
</div>
);
}
}
看起来临时变量起作用了,很奇怪。
const tempTabs = [...this.tabs];
if (onHead) {
tempTabs.unshift(tab);
} else {
tempTabs.push(tab);
}
this.tabs = tempTabs;
Stencil 执行严格的相等性检查 (===
) 以确定 Prop/State 变量是否已更改,这就是它不检测 push
和 unshift
作为更改的原因.您必须确保用新阵列替换阵列。在您的示例中执行此操作的最快方法是在操作后用副本手动替换数组:
if (onHead) {
this.tabs.unshift(tab);
} else {
this.tabs.push(tab);
}
this.tabs = [...this.tabs];
这是引用相等的问题。对象总是通过引用而不是值传递,因此两个对象永远不会相等(reference-wise,即使它们包含相同的值)。
数组是一种特殊的对象,因此也是通过引用传递的。修改数组的值不会更改其引用。
一些例子来说明这一点:
const foo = ['a', 'b'];
console.log(foo === ['a', 'b', 'c']); // false
foo.push('c');
console.log(foo === ['a', 'b', 'c']); // still false
console.log(['a', 'b', 'c'] === ['a', 'b', 'c']); // actually always false
console.log(foo === foo); // always true because it is the same reference
Stencil 使用相同的严格相等运算符 ===
比较 @State()
修饰的 class 成员(@Prop()
也是如此)。如果值相同,则组件不是 re-rendered.
对于您的 tabs
状态,this.tabs
的值是对您分配给它的数组的引用。修改数组(例如 this.tabs.push(...)
)只会更改 this.tabs
引用的数组的值,但不会更改存储在 this.tabs
.
中的实际引用
因此您需要 re-assign this.tabs
才能让 Stencil 知道该成员已更改。最简单的方法是
this.tabs = [...this.tabs];
将数组的值分散到一个新数组中(returns 一个新的引用)。或者像 this.tabs = this.tabs.slice()
这样的东西也可以解决问题(任何 returns 新数组都有效)。
在您的情况下,最简单的方法是将 addTab
方法更改为
addTab(onHead = true) {
const tab = (
<li role="presentation" class="nav-item">
<button id="tab3" class="btn btn-link nav-link" aria-controls="panel3"
aria-selected="false" type="button">Dashboard</button>
</li>
);
this.tabs = onHead ? [tab, ...this.tabs] : [...this.tabs, tab];
}
(即在新项目之前或之后传播原始值)。
import { Component, h, State } from '@stencil/core';
// import '@webcomponents/custom-elements';
import '@clr/core/icon/register';
import { ClarityIcons, plusIcon } from '@clr/core/icon';
ClarityIcons.addIcons(plusIcon);
@Component({
tag: 'tabs-component',
styleUrl: 'tabs-component.css',
shadow: false,
})
export class TabsComponent {
@State() tabs: Array<object> = [
(
<li role="presentation" class="nav-item">
<button id="tab3" class="btn btn-link nav-link" aria-controls="panel3"
aria-selected="false" type="button">Cloud</button>
</li>
)
];
addTab(onHead = true) {
// debugger
const tab = (
<li role="presentation" class="nav-item">
<button id="tab3" class="btn btn-link nav-link" aria-controls="panel3"
aria-selected="false" type="button">Dashboard</button>
</li>
);
if (onHead) {
this.tabs.unshift(tab);
} else {
this.tabs.push(tab);
}
console.log(this.tabs);
}
render() {
return (
<div>
<ul id="demoTabs" class="nav" role="tablist">
<li role="presentation" class="nav-item" onClick={() => this.addTab()}>
<cds-icon shape="plus" class="cursor"></cds-icon>
</li>
{this.tabs}
</ul>
<section id="panel1" role="tabpanel" aria-labelledby="tab1">
tab1
</section>
<section id="panel2" role="tabpanel" aria-labelledby="tab2" aria-hidden="true">
tab2
</section>
<section id="panel3" role="tabpanel" aria-labelledby="tab3" aria-hidden="true">
tab3
</section>
</div>
);
}
}
看起来临时变量起作用了,很奇怪。
const tempTabs = [...this.tabs];
if (onHead) {
tempTabs.unshift(tab);
} else {
tempTabs.push(tab);
}
this.tabs = tempTabs;
Stencil 执行严格的相等性检查 (===
) 以确定 Prop/State 变量是否已更改,这就是它不检测 push
和 unshift
作为更改的原因.您必须确保用新阵列替换阵列。在您的示例中执行此操作的最快方法是在操作后用副本手动替换数组:
if (onHead) {
this.tabs.unshift(tab);
} else {
this.tabs.push(tab);
}
this.tabs = [...this.tabs];
这是引用相等的问题。对象总是通过引用而不是值传递,因此两个对象永远不会相等(reference-wise,即使它们包含相同的值)。
数组是一种特殊的对象,因此也是通过引用传递的。修改数组的值不会更改其引用。
一些例子来说明这一点:
const foo = ['a', 'b'];
console.log(foo === ['a', 'b', 'c']); // false
foo.push('c');
console.log(foo === ['a', 'b', 'c']); // still false
console.log(['a', 'b', 'c'] === ['a', 'b', 'c']); // actually always false
console.log(foo === foo); // always true because it is the same reference
Stencil 使用相同的严格相等运算符 ===
比较 @State()
修饰的 class 成员(@Prop()
也是如此)。如果值相同,则组件不是 re-rendered.
对于您的 tabs
状态,this.tabs
的值是对您分配给它的数组的引用。修改数组(例如 this.tabs.push(...)
)只会更改 this.tabs
引用的数组的值,但不会更改存储在 this.tabs
.
因此您需要 re-assign this.tabs
才能让 Stencil 知道该成员已更改。最简单的方法是
this.tabs = [...this.tabs];
将数组的值分散到一个新数组中(returns 一个新的引用)。或者像 this.tabs = this.tabs.slice()
这样的东西也可以解决问题(任何 returns 新数组都有效)。
在您的情况下,最简单的方法是将 addTab
方法更改为
addTab(onHead = true) {
const tab = (
<li role="presentation" class="nav-item">
<button id="tab3" class="btn btn-link nav-link" aria-controls="panel3"
aria-selected="false" type="button">Dashboard</button>
</li>
);
this.tabs = onHead ? [tab, ...this.tabs] : [...this.tabs, tab];
}
(即在新项目之前或之后传播原始值)。