既存の Razor コンポーネントから "派生" した Razor コンポーネントを作る

## Razor コンポーネントは C# クラスなので派生できる

既存の Razor コンポーネントに対して、あとちょっと機能を足したいという場合がままあります。そのような場合は、C# の拡張メソッドを実装するとか、その既存の Razor コンポーネントを内包・ラップした新たな Razor コンポーネントを作る、といった方法があります。しかし、そう滅多にあることではないのですが、希に、既存の Razor コンポーネントから "派生" した新たな Razor コンポーネントを作りたい、という場合があります。

幸い、Razor コンポーネントは、つまるところは、単なる C# のクラスに過ぎません。例えば `<Foo />` という Razor コンポーネントは、つまりは `Foo` クラスです。これから派生した新たな Razor コンポーネント、`<Bar />` を作りたい、という場合は、プロジェクトに `Bar.cs` という C# ソースファイルを追加し、そこで、普通に C# の構文で「`Foo` クラスから派生した `Bar` クラス」を実装すれば実現できます (下記コード例)。

```cs:Bar.cs
public class Bar : Foo {
   // ここに追加の実装を記述
}
```

## .cs で実装すると *.razor.js が `error BLAZOR106` を引き起こす

しかし、さらにここで、その派生して作成した `<Bar />` に固有の JavaScript モジュールファイル、`Bar.razor.js` を併設したいとしましょう。C# 側のソースコードは以下のようなイメージです。

```cs:Bar.cs
public class Bar : Foo {

  [Inject]
  public IJSRuntime JSRuntime { get; init; } = null!;

  protected override async Task OnAfterRenderAsync(bool firstRender)
  {
    await base.OnAfterRenderAsync(firstRender);
    
    if (firstRender)
    {
      // 👇 JavaScript モジュールファイル、Bar.razor.js を読み込む
      await using var module = await this.JSRuntime.InvokeAsync<IJSObjectReference>(
                                                               "import", "./Bar.razor.js");

      // ここで、Bar.razor.js 内で export された関数を呼び出したりする
      // 例) await module.InvokeAsync<...>(...);
    }
  }
}
```

ということで、プロジェクトに `Bar.razor.js` を追加してビルドしてみたところ、残念ながら以下のビルドエラーが発生してしまいました。

```
error BLAZOR106:
The JS module file '...\Bar.razor.js' was defined but no associated razor component or
view was found for it.
```

どうやら、Blazor アプリケーションとしてのビルド処理において、*.razor.js のような、Razor コンポーネントに抱き合わせの併設ファイルを配置するには、その親 (?) となる *.razor ファイルが実在していないとダメらしいのです。

では、ということで、C# ソースファイル、すなわち、.cs ファイルではなく、Razor コンポーネント形式、すなわち、.razor ファイルで、`Bar` コンポーネントを実装してみることにします。Razor コンポーネント (.razor ファイル) においてクラスの派生を用いるには、`@inherits` ディレクティブを使えばよいです。ということで、実装は以下のようになります。

```cs:Bar.razor
@inherits Foo

@code {
  // 以下略
}
```

これで無事ビルドがとおるようになり、`Bar.razor.js` を import して使えるようになります。

## .cs ではなく .razor で派生すると、派生元コンポーネントのレンダリングが現れなくなる

しかし今度は、せっかく `<Foo />` コンポーネントから派生したのに、`<Foo />` コンポーネントとしてのレンダリング結果が出力されません。というのも、Razor コンポーネント、すなわち、.razor ファイルを Razor ソースジェネレーターが C# ソースコードに変換する際は、`BuildRenderTree` メソッドもオーバーライドして、.razor ファイル内に記述されたマークアップをレンダリングするようにコード生成されるからなのですね。つまり、`Bar.razor` 内のマークアップをレンダリングするよう `BuildRenderTree` メソッドが上書きされるため、`<Foo />` コンポーネントで実装されていた `BuildRenderTree` メソッドの処理内容は一切呼び出されず使用されないわけです。

そこで、`<Foo />` コンポーネントで実装されていた `BuildRenderTree` の処理内容を、派生先の `<Bar />` コンポーネントでも今一度明示的にレンダリングするように、実装を追加します。

まず、派生元のレンダリング処理を保持する `RenderFragment` 型のフィールド変数を用意し、

```cs:Bar.razor
@inherits Foo

@code {
  // 👇 RenderFragment フィールド変数を用意
  private RenderFragment _baseRenderer;
  ...
}
```

続けて、コンストラクタを追加して、そこで派生元のレンダリング処理、すなわち、派生元の `BuildRenderTree` メソッド呼び出しを、用意しておいたフィールド変数に格納します。

```cs:Bar.razor
@inherits Foo

@code {
  private RenderFragment _baseRenderer;

  // 👇コンストラクタを追加し...
  public Bar() {

    // 👇派生元の BuildRenderTree を呼び出す RenderFragment (ラムダ式) を作って
    //   先に用意したフィールド変数に格納
    _baseRenderer = (builder) => base.BuildRenderTree(builder);
  }
  ...
}
```

そうしてフィールド変数に格納された `RenderFragment` を、派生先 .razor 内のマークアップにてレンダリングします。

```cs:Bar.razor
@inherits Foo

<!--
// 👇フィールド変数に格納された、派生元の BuildRenderTree を呼び出す RenderFragment を、
//   マークアップ内でレンダリング -->
@_baseRenderer

@code {
  private RenderFragment _baseRenderer;

  public Bar() {
    _baseRenderer = (builder) => base.BuildRenderTree(builder);
  }
  ...
}
```

## おわりに

以上の実装にて、既存の Razor コンポーネントから "派生" した新たな Razor コンポーネントを実装することができました。

そうそう出番のある技法とも思いませんが、バイナリでしか提供されていない既存の Razor コンポーネントにモンキーパッチを当てたい、などのケースで使えたりします。ご参考までに。