6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

BlazorAdvent Calendar 2024

Day 19

Blazor - URL パラメータ違いの同じルート URL に遷移すると、表示が更新されない??

Last updated at Posted at 2024-12-18

「前の記事」「次の記事」のリンクを追加してみた

先日、Blazor WebAssembly で Advent Calendar アプリを作る記事が Blazor Advent Calendar 2024 に投稿されました。

これをちょっと改造しまして、以下のように、「前の記事」「次の記事」のリンクを、記事ページの右上に設けてみました。

image.png

実装は次のようにしました。

まず、記事ページの Razor コンポーネント中、@code ブロック内に、「前の記事」「次の記事」それぞれの URL を保持するフィールド変数を追加します。

Pages/Post.razor
...
@code {
    ...
    private string? prevPostUrl;
    
    private string? nextPostUrl;
    ...

そして、OnInitializedAsync ライフサイクルメソッド内で、URL パラメータに渡された日付を元に、「前の記事」「次の記事」それぞれの URL を生成して、先のフィールド変数に格納します。

Pages/Post.razor
...
@code {
    ...
    protected override async Task OnInitializedAsync()
    {
        // 前後の記事へのリンクを生成
        prevPostUrl = Day > 1 ? $"/post/{Day - 1}" : null;
        nextPostUrl = Day < 25 ? $"/post/{Day + 1}" : null;
        ...

あとは、これら「前の記事」「次の記事」それぞれの URL を保持するフィールド変数を、<a> 要素の href 属性にバインドして完成です。

Pages/Post.razor
...
<SectionContent SectionName="AppBar">
    <div class="quick-nav-link">
        <a href="@prevPostUrl">前の記事</a>
        <span>|</span>
        <a href="@nextPostUrl">次の記事</a>
    </div>
</SectionContent>
...

表示が更新されない??

早速に動作確認してみます。1日目の記事 (/post/1) を開いた状態で、「次の記事」のリンク (/post/2) をクリックしてみると...

image.png

おやおや、ブラウザのアドレスバーに表示されている URL は、ちゃんと 2 日目の記事を指しているにもかかわらず、記事ページ本体は 1 日目の記事を表示したまま変わりません。 開発者コンソールなどで確認するもエラーは起きていないようです。しかも、ページを再読み込みすると 2 日目の記事がちゃんと表示されます。いったい何がいけないのでしょうか。

URL パラメータが変わるだけなら Razor コンポーネントは作り直されない

実は Blazor では、URL パラメータ違いの同じルート URL に遷移した場合、そのルート URL にマッチした Razor コンポーネントのインスタンスは作り直されないのです。代わりに、その Razor コンポーネントのインスタンスの、URL パラメータをバインドしているプロパティ値が書き換わるだけなのです。

例えば、"/" から "/post/1" にページ遷移して、記事ページがはじめて表示されるとき、そのタイミングで Post Razor コンポーネントのインスタンスが生成され、Day プロパティには 1 が設定されます。

Pages/Post.razor
@page "/post/{day:int}"
...
@code {
    ...
    [Parameter]
    public int Day { get; set; } // 👈 1 が格納される
    ...

その上で、OnInitializedAsync をはじめとしたライフサイクルメソッドが次々と呼び出され、DOM ツリーへの描画が実施されます。

さてここで、URL "/post/1" から "/post/2" に遷移するときなのですが、このときは、記事ページをレンダリングした Post Razor コンポーネントのインスタンスは同じインスタンスが使われたまま、Day プロパティが 2 に設定された上で、再描画が行なわれます。

しかしこのとき、OnInitializedAsync は呼び出されません。 何となれば、その Razor コンポーネントのインスタンスは既に "初期化済み" だから、です。

Pages/Post.razor
@page "/post/{day:int}"
...
@code {
    ...
    [Parameter]
    public int Day { get; set; } // 👈 2 が格納される...が、

    // 👇 OnInitializedAsync は呼ばれない!
    protected override async Task OnInitializedAsync() {
        // (ここで記事ソースの取得や整形などを行なっている)
        ...

これが「前の記事」「次の記事」リンクをクリックしても、記事の表示が変わらない理由です。

ではこのようなシナリオにおいてはどう対処するのがよいのでしょうか。

OnParameterSetAsync を使おう

Blazor のライフサイクルメソッドのひとつに、OnParameterSetAsync というメソッドがあります。これは、その Razor コンポーネントのパラメータが設定されるときに呼び出されます。

つまり、URL "/post/" から "/post/2" への遷移によって、記事ページコンポーネントの Dayプロパティが 1 から 2 に設定されるときにも、このOnParameterSetAsync` ライフサイクルメソッドが呼び出されます。

ですので、これまで OnInitalizedAsync で行なってきた、記事ソースの取得や整形などの処理を、OnParameterSetAsync で行なうようにプログラムを修正するとよいです。

Pages/Post.razor
@page "/post/{day:int}"
...
@code {
    ...
    [Parameter]
    public int Day { get; set; } // 👈 2 が格納されるときに...

    // 👇 OnParametersSetAsync は呼ばれる!
    protected override async Task OnParametersSetAsync() {
        await base.OnParametersSetAsync();
        ...

なお、基底クラスの OnParametersSetAsync を呼び出したあとでないと、[Parameter] 属性を付与したプロパティへのパラメータ値の設定・反映は行なわれませんので、その点はご注意ください。

これで解決です。ちゃんと「前の記事」「次の記事」のリンクが動作するようになりました。

なお今回は変動しうるパラメータが Day プロパティのみということもあって、雑に、OnIntializedAsync で行なっていた処理をまるっと OnParameterSetAsync に引っ越しして済ませましたが、本来は、どのパラメータに変更があったのかとか、そのパラメータ値の変更によってどんな処理を行なうか、など、よく検討の上、適切に実装する必要があるはずです。場合によっては、ShouldRender ライフサイクルメソッドもオーバーライドして、再描画回数の低減を行なう必要も出てくるかもしれません。

以上、おつかれさまでした。

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?