◆ key を使う
  ◆ 変えるとコンポーネントを再作成してくれる
◆ 配列の map 以外でも並び替えるために必要
  ◆ 埋め込むインスタンスを指定するわけじゃないので同じものを判断するのに必要
  ◆ props のみならいいけど state が入ると引き継げない

以前から React を使うと親コンポーネントから簡単に子コンポーネントの状態をリセットできないことが不便でした
React で親コンポーネントから子コンポーネントの state を更新したい
React 系の state がクリアされない問題

これを簡単に解決できる方法がありました
key を使います

key でリセット

key は配列を map して要素の配列として埋め込むときに同じものであることを判断するために使うものくらいのイメージでした
しかし それ以外の場所でも使えて key が前回と異なればコンポーネントを作り直してくれます
以前は自分で key 的なものを管理して 違いがあれば state の更新で初期値を手動で入れてましたが そんな苦労も不要でした

key はリセットしない限りは前回と同じで リセットのタイミングでのみ前回と異なる必要があります
管理が楽になるように useReset フックを作りました
中では useState を使ってリセット時に新しいオブジェクトをセットしています
最初は目に見える数値などにしようかと思いましたが オブジェクトだとランダム値よりも確実に毎回異なるものになりますし 数値をインクリメントだとグループごとにリセットしたいときに別グループと同じ番号になることで競合が発生しておかしな挙動になることがあったのでやめました

<!DOCTYPE html>

<script type="module">
import { html, render, useState } from "https://unpkg.com/htm/preact/standalone.module.js"

const Counter = () => {
const [count, setCount] = useState(0)

return html`
<div>
<span>${count}</span>
<button onclick=${() => setCount(count + 1)}>Up</button>
</div>
`
}

const useReset = () => {
const [id, setId] = useState({})
return [id, () => setId({})]
}

const App = () => {
const [reset_key, reset] = useReset()
const [reset_key2, reset2] = useReset()

return html`
<${Counter} key=${reset_key} />
<${Counter} key=${reset_key} />
<${Counter} key=${reset_key} />
<button onclick=${reset}>Reset</button>
<${Counter} key=${reset_key2} />
<button onclick=${reset2}>Reset2</button>
`
}

render(html`<${App} />`, document.getElementById("app"))
</script>

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

useReset で返ってくる配列の 2 つめがリセット関数でこれを呼び出せば reset_key が更新されます
まとめてリセットしたいものは同じ reset_key を key にセットしておくとまとめてリセットできます

また この場合はリセット後も Counter 要素なので div や span の構造は同じです
なので div などは再利用されて値だけ変わるのかと思いきや Counter が作る DOM 全体が新しいものになっていました
作り直しで同じコンポーネントであることはあまりなさそうなので 1 つ 1 つ要素の一致をチェックしていくよりは コンポーネントの作り直しなら完全に 1 から作って置き換えたほうが速いという判断なのかもしれません
WebComponents で作った場合に Custom Element を replaceWith で新規のものに置き換えるようなものだと考えると良いと思います

その他配列以外で key を使う例

他にも配列を map せず単純に並び替える場合ときでも使えました

<!DOCTYPE html>

<script type="module">
import { html, render, useState } from "https://unpkg.com/htm/preact/standalone.module.js"

const Counter = ({ name }) => {
const [count, setCount] = useState(0)

return html`
<div>
<span>${name}:</span>
<span>${count}</span>
<button onclick=${() => setCount(count + 1)}>Up</button>
</div>
`
}

const App = () => {
const [reverse, setReverse] = useState(false)

return html`
<div>
${
reverse
? html`
<${Counter} name="counter2"/>
<${Counter} name="counter1"/>
`
: html`
<${Counter} name="counter1"/>
<${Counter} name="counter2"/>
`
}
<button onclick=${() => setReverse(!reverse)}>Change</button>
</div>
`
}

render(html`<${App} />`, document.getElementById("app"))
</script>

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

App の ${} の部分です
2 つのカウンターを並び替えているのですが インスタンスを指定しているわけじゃないので 並び替わりません
プロパティとして渡される name が違うので name が変わりますが state に入ってるカウントは上側のものは上側のままで下側のものは下側のままです

counter1: 10
counter2: 3

でボタンを押して並び替えても

counter2: 10
counter1: 3

になります

ここで Counter に key をつけて並び替えると state も入れ替わってくれます

	${
reverse
? html`
<${Counter} key="2" name="counter2"/>
<${Counter} key="1" name="counter1"/>
`
: html`
<${Counter} key="1" name="counter1"/>
<${Counter} key="2" name="counter2"/>
`
}

counter1: 10
counter2: 3

でボタンを押すと

counter2: 3
counter1: 10

たいしてつかってなかった key ですが結構重要ですね

プレビューで試せる全体版です

<!DOCTYPE html>

<script type="module">
import { html, render, useState } from "https://unpkg.com/htm/preact/standalone.module.js"

const Counter = ({ name }) => {
const [count, setCount] = useState(0)

return html`
<div>
<span>${name}:</span>
<span>${count}</span>
<button onclick=${() => setCount(count + 1)}>Up</button>
</div>
`
}

const App = () => {
const [reverse, setReverse] = useState(false)

return html`
<div>
${
reverse
? html`
<${Counter} key="2" name="counter2"/>
<${Counter} key="1" name="counter1"/>
`
: html`
<${Counter} key="1" name="counter1"/>
<${Counter} key="2" name="counter2"/>
`
}
<button onclick=${() => setReverse(!reverse)}>Change</button>
</div>
`
}

render(html`<${App} />`, document.getElementById("app"))
</script>

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

おまけ: lit-element の場合

この問題は lit-element で困ったことがないなと思っていたので試してみました

lit-element でも単純にタグを書いてプロパティを渡すだけでは同じことになりました
要素が並び替わってるわけではないので要素が内部に保持しているプロパティは元の並び通りです

	${
this.reverse
? html`
<child-elem .name=${"B"}></child-elem>
<child-elem .name=${"A"}></child-elem>
`
: html`
<child-elem .name=${"A"}></child-elem>
<child-elem .name=${"B"}></child-elem>
`
}

実行用の全体

<!DOCTYPE html>

<script type="module">
import { html, LitElement } from "https://unpkg.com/lit-element@2.4.0/lit-element.js?module"

customElements.define("child-elem", class extends LitElement {
static get properties() {
return {
id: { type: String },
name: { type: String },
}
}

id = Math.random().toString(16).slice(2)

render() {
return html`
<div>
[${this.id}]
name: ${this.name}
</div>
`
}
})

customElements.define("root-elem", class extends LitElement {
static get properties() {
return {
reverse: { type: Boolean },
}
}

render() {
return html`
<div>
<div>
<button @click=${() => this.reverse = !this.reverse}>Change</button>
</div>
${
this.reverse
? html`
<child-elem .name=${"B"}></child-elem>
<child-elem .name=${"A"}></child-elem>
`
: html`
<child-elem .name=${"A"}></child-elem>
<child-elem .name=${"B"}></child-elem>
`
}
</div>
`
}
})
</script>

<root-elem></root-elem>

ですが lit-element の場合は直接 DOM インスタンスを埋め込めます
chile-elem のインスタンスを保持しておいて

	${
this.reverse
? html`
${this.child2}
${this.child1}
`
: html`
${this.child1}
${this.child2}
`
}

のように書けば要素自体を並び替えてくれます
もちろん要素を並び替えるのでこれだと disconnectedCallback と connectedCallback が呼び出されます

実行用の全体

<!DOCTYPE html>

<script type="module">
import { html, LitElement } from "https://unpkg.com/lit-element@2.4.0/lit-element.js?module"

customElements.define("child-elem", class extends LitElement {
static get properties() {
return {
id: { type: String },
name: { type: String },
}
}

id = Math.random().toString(16).slice(2)

render() {
return html`
<div>
[${this.id}]
name: ${this.name}
</div>
`
}
})

customElements.define("root-elem", class extends LitElement {
static get properties() {
return {
reverse: { type: Boolean },
}
}

constructor() {
super()
this.child1 = document.createElement("child-elem")
this.child1.name = "A"
this.child2 = document.createElement("child-elem")
this.child2.name = "B"
}

render() {
return html`
<div>
<div>
<button @click=${() => this.reverse = !this.reverse}>Change</button>
</div>
${
this.reverse
? html`
${this.child2}
${this.child1}
`
: html`
${this.child1}
${this.child2}
`
}
</div>
`
}
})
</script>

<root-elem></root-elem>

この記事の主題のリセットの方については lit-element なら WebComponents なので子要素に reset メソッドを作ってそれを呼び出してもいいですし 上の例のようにタグを書かずに要素を埋め込む形式にしてリセットのタイミングで cloneNode したものに置き換えでもいいです