最初にrefをおさらいする。例えば次のような感じに実装しているとしよう。
class FooComponent extends React.Component<{}, {}> {
bar: React.createRef<BarComponent>;
constructor(props: any) {
super(props)
this.bar = React.createRef<BarComponent>();
}
render() {
return <BarComponent ref={this.bar} />
}
}
mobx-reactのinjectデコレータを使っていないコンポーネントに対してこのようにrefでインスタンスを取得した場合はReact.RefObject<T>でラップされたインスタンスが得られるのでthis.bar.current
のようにしてコンポーネントにアクセスすることができる。
ところがBarComponentがinjectデコレータを使っているコンポーネントの場合、this.bar.current
にはmobx-reactで生成されたInjectorという名のオブジェクトが注入される。InjectorはwrappedInstanceというメンバーで参照したいコンポーネントのインスタンスをもっているため、この場合はthis.bar.current.wrappedInstance
というようにアクセスすればよい。
と、ここまでは特に難しいことではないのだがTypescriptの場合に困った問題が生じる。上の実装例のまま実際にコンポーネントを参照するロジックを実装しようとするとcurrentにwrappedInstanceなんていうメンバーはないと怒られてしまう。
それならば、InjectorはIWrappedComponent<T>というインターフェイスのオブジェクトなので次のように宣言を変更すると...
class FooComponent extends React.Component<{}, {}> {
bar: React.createRef<IWrappedComponent<BarComponent>>;
constructor(props: any) {
super(props)
this.bar = React.createRef<IWrappedComponent<BarComponent>>();
}
render() {
return <BarComponent ref={this.bar} />
}
}
今度はrender内のrefの箇所でBarComponentを割り当てられないよと怒られる。(実際はLegacyRef<BarComponent>をIWrappedComponent<BarComponent>に割り当てられないというメッセージが出る。)
mobx-reactのinjectデコレータを使うと宣言と異なるクラスのインスタンスが注入されるため、真面目に宣言しようとするとこのようになる。Typescriptのメリットが失われてしまうのは残念だが、injectデコレータを使っているコンポーネントをrefで参照したい場合はanyで宣言する。
class FooComponent extends React.Component<{}, {}> {
bar: React.createRef<any>;
constructor(props: any) {
super(props)
this.bar = React.createRef<any>();
}
render() {
return <BarComponent ref={this.bar} />
}
}