◆ 親コンポーネントから子コンポーネントのメソッド呼べればいいのに
◆ インスタンスの参照を取得できない

React とか Vue とか見てても hyperHTML や lit-html でいいじゃんと思って 対して触れてなかったのですが これらは最低限の DOM 更新部分だけで後は好きにできるものです
またユーザもフレームワークに比べて少なく情報も少ないです
コンポーネントにするにしてもどうやるのがベストなんだろうと考えることも多いです

具体的な書き方はフレームワークやライブラリに依存しますが どういうふうにコンポーネントを分けるとか作り方的な部分は基本どれでも一緒だと思うので 参考になる情報が多いフレームワークを触れておいたほうがいいかなと 思いつきでちょっと React いじってます

以前は JSX が嫌で代わりにテンプレートリテラル使うとか補助ライブラリ風なものを作るだけで満足して結局使ってないですからねー

子コンポーネントのインスタンスを持ってない

実際に使ってみて気になった部分は WebComponents と違って 親が子のインスタンスにアクセスできないところです
WebComponents の場合は ShadowRoot の querySelector でカスタム要素を取得すればそれが子コンポーネントのインスタンスです
そのメソッドを呼び出して命令を実行できます

これで困ったのが 親コンポーネントが子コンポーネントのデータを操作できない部分です
以前 WebComponents なしで hyperHTML で作ったときは コンポーネントごとの状態をコンポーネント内部ではもたず すべての情報がひとつのオブジェクトに含まれてるというものにしてました
現在のタブ内の情報や編集中テキストなどもそのオブジェクトのプロパティのどこかに含まれてます
全コンポーネントがどこの情報を参照するとか決まってしまっていて そのプロジェクト内ではよくても他に持っていって再利用するのは難しいものでした
その分どこからでもアクセスできるので ヘッダーにあるボタンをクリックしたときに現在のタブの中の状態を変えるというコンポーネントを超えたデータ操作も簡単ではありました

これも一つのやり方と思ってましたが React だとちゃんと state というものがありますし 基本外側が知る必要ないものはコンポーネントの中にとどめておこうという考えにしてます
ただ 具体的に子コンポーネントのデータを見てどうこうすることはしなくても 編集中データをクリアする命令を親から送りたいとかはあります
親で操作するとなると React だと props で親から子にデータを渡せるので これを使って親側でクリアして子に渡すことはできます
ただ 他で使わないコンポーネント内部情報を親で持つのもなぁと思います
クリアと言っても初期値がなにかとか そういう情報は子コンポーネントの中に収めたいです

子コンポーネントのインスタンスを持ってれば メソッドを呼び出せるので clear メソッドを呼び出せばあとは 子コンポーネント側でどの値を何にセットするとか操作できます
それかイベントを子コンポーネントへ通知できれば 子コンポーネントでそれを受け取って子コンポーネントで処理できるのですが それらしい方法が見当たらないです
対して使い込んでないのでもしかするとあるのかもですが ガイドとかチュートリアルの機能説明を眺めた感じなさそうです

状態の一つとしてイベントをもたせてそれを下流コンポーネントへ伝播させて 各コンポーネントでチェックしていくというのも考えましたが その情報はどのタイミングでクリアするの?とか 状態なんだし今の情報を示すのであって◯◯をするみたいな情報を持つのは違うなと思ったりで 結局これはやめました
全部の props に event_queue みたいのを持たせるのも大変ですし

子コンポーネントインスタンスを参照する

結局どうにか子コンポーネントのインスタンスの参照取得する方法でがんばることにしました

サンプルとしてこんなのを用意しました
Parent と Child の 2 種類のコンポーネントがあります
Parent は 3 つの Child を表示して Child では a と b の 2 つの状態を持っています
Child のボタンを押すと a と b の両方をインクリメントして Parent のボタンを押すとすべての Child の a を 0 にリセットします

公式にあったサンプルをベースにしたのでコンパイル必要ですが jsx 使用になってます

class Parent extends React.Component {
render() {
return (
<div>
<div>
<Child/>
<Child/>
<Child/>
</div>
<button>Clear A</button>
</div>
)
}
}

class Child extends React.Component {
state = { a: 0, b: 0 }
up = () => {
this.setState(s => ({a: s.a + 1, b: s.b + 1}))
}
render() {
return (
<div>
{this.state.a},
{this.state.b}
<button onClick={this.up}>Up</button>
</div>
)
}
}

ReactDOM.render(
<Parent/>,
document.getElementById("root")
)

現時点ではこれはリセット機能が動きません
どうにかしてリセットしたいです

インスタンスを埋め込んで render できる?

子コンポーネントを作るところはインスタンスではなくコンポーネントの関数やクラスのみを指定します
インスタンスを作ったりは内部で管理されているのでインスタンスの参照は取得できません

ですが hyperHTML や lit-html では ${} に DOM の要素を入れることができます
そう考えたら React でもコンポーネントのインスタンスを children に指定できるのではないでしょうか
ということで試してみたのがこれです

class Parent extends React.Component {
child1 = new Child()
child2 = new Child()
child3 = new Child()
clear = () => {
child1.clear()
child2.clear()
child3.clear()
}
render() {
return (
<div>
<div>
{this.child1}
{this.child2}
{this.child3}
</div>
<button onClick={this.clear}>Clear A</button>
</div>
)
}
}

class Child extends React.Component {
state = { a: 0, b: 0 }
up = () => {
this.setState(s => ({a: s.a + 1, b: s.b + 1}))
}
clear() {
this.setState({ a: 0 })
}
render() {
return (
<div>
{this.state.a},
{this.state.b}
<button onClick={this.up}>Up</button>
</div>
)
}
}

無理でした
children にはコンポーネントのインスタンスをいれることはできなくて入れるとエラーになりました

参照を子コンポーネントで入れてもらう

子コンポーネントに関数を渡すことができて それを呼び出す子コンポーネントでは this を使って子コンポーネント自身にアクセスできます
なので 参照を集めるハンドラを子コンポーネントに渡してコンストラクタで this 入れて呼び出してもらいます

class Parent extends React.Component {
children = []
clear = () => {
this.children.forEach(c => c.clear())
}
addChildRef = (instance) => {
this.children.push(instance)
}
render() {
return (
<div>
<div>
<Child onInit={this.addChildRef} />
<Child onInit={this.addChildRef} />
<Child onInit={this.addChildRef} />
</div>
<button onClick={this.clear}>Clear A</button>
</div>
)
}
}

class Child extends React.Component {
state = { a: 0, b: 0 }
constructor(props) {
super(props)
props.onInit(this)
}
up = () => {
this.setState(s => ({a: s.a + 1, b: s.b + 1}))
}
clear() {
this.setState({ a: 0 })
}
render() {
return (
<div>
{this.state.a},
{this.state.b}
<button onClick={this.up}>Up</button>
</div>
)
}
}

これで動きました
ただ onInit がいるのがネックです

React.Component を継承したベースクラス作ってそこで onInit があれば this 入れて呼び出すようにしておけばマシになりそうですが なんかベストプラクティスって感じはしません

探してみると DOM の要素そのものを参照するための ref という機能があるみたいで もしかしたらこれを使ってユーザコンポーネントの参照も取れるのかもしれません
できたとしても createRef を呼び出して作ったり 準備が面倒な感じでした

もっとシンプルに name プロパティを設定すると this.components[name] で取得できたりみたいなのはないのですかねー
ここまで簡単に子コンポーネントの参照が取れないあたり そもそも子コンポーネントのメソッドを呼び出すようなことは React では想定されてないのかもしれないです

追記

ref が正しい方法みたいです
DOM だけじゃなくコンポーネントでもできました
複雑だと思ってたのは見つけた例外がフォワードとかしてたからでシンプルなケースを見るとそこまで不便でもなかったです
それでもやっぱり name プロパティを設定したらその名前でプロパティ参照で取得できるのがいいのですけどね

簡単な例です

const e = React.createElement
const root = document.getElementById("root")

const P = props => {
const ref = React.useRef()
const onClick = () => ref.current.method()
return e("div", { onClick }, e(C, { ref }))
}

class C extends React.Component {
state = { x: 0 }

render() {
return e("p", {}, this.state.x)
}

method() {
this.setState({ x: Math.random() })
}
}

ReactDOM.render(e(P), root)

P の div クリックで C の method が呼び出されて C の state が更新されて表示される数値が変わります

ref が使えるのはクラスコンポーネントのみで 関数だとインスタンスがないわけですし ref.current では何も取得できませんでした
関数コンポーネントで useState 使ってるときだとどうすればいいんでしょうね
useState で取得した更新関数を props で受け取るプロパティのプロパティにセットすれば親から参照できますが これじゃない感があります

const e = React.createElement
const root = document.getElementById("root")

const P = props => {
const myref = { call(...a) { this.fn && this.fn(...a) } }
const onClick = () => myref.call()
return e("div", { onClick }, e(C, { myref }))
}

const C = props => {
const [x, setX] = React.useState(0)
props.myref.fn = () => setX(Math.random())
return e("p", {}, x)
}

ReactDOM.render(e(P), root)

C を使う側が myref オブジェクトを渡す前提になってますし