简体   繁体   English

Blazor 组件参考 Null 首次渲染

[英]Blazor Component Reference Null on First Render

I have a custom component with an event Action called TabChanged.我有一个自定义组件,其中包含一个名为 TabChanged 的事件操作。 In my Razor page I set the reference to it up like so:在我的 Razor 页面中,我设置了对它的引用,如下所示:

<TabSet @ref="tabSet">
 ...
</TabSet>

@code {
    private TabSet tabSet;   
    ...
}

In the OnAfterRenderAsync method I assign a handler to the event:OnAfterRenderAsync方法中,我为事件分配了一个处理程序:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if(firstRender)
    {
        tabSet.TabChanged += TabChanged;
    }       
}

The first time the page renders I get a System.NullReferenceException: Object reference not set to an instance of an object error.第一次呈现页面时,我收到System.NullReferenceException: Object 引用未设置为 object 错误的实例

If I switch to use subsequent renders it works fine:如果我切换到使用后续渲染它工作正常:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if(!firstRender)
    {
        tabSet.TabChanged += TabChanged;
    }       
}

But of course this is sloppy and I will be firing multiple event handlers as they stack up during renders.但当然这是草率的,我将在渲染期间触发多个事件处理程序。

How can I assign the reference one time and on first render?如何在第一次渲染时一次性分配参考? I am following the docs as outlined here我正在关注此处概述的文档

EDIT编辑

Here is the TabSet.razor file:这是 TabSet.razor 文件:

@using Components.Tabs

<!-- Display the tab headers -->
<CascadingValue Value="this">
    <ul class="nav nav-tabs">
        @ChildContent
    </ul>
</CascadingValue>

<!-- Display body for only the active tab -->
<div class="nav-tabs-body" style="padding:15px; padding-top:30px;">
    @ActiveTab?.ChildContent
</div>

@code {

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public ITab ActiveTab { get; private set; }

    public event Action TabChanged;


    public void AddTab(ITab tab)
    {
        if (ActiveTab == null)
        {
            SetActiveTab(tab);
        }
    }

    public void RemoveTab(ITab tab)
    {
        if (ActiveTab == tab)
        {
            SetActiveTab(null);
        }
    }

    public void SetActiveTab(ITab tab)
    {
        if (ActiveTab != tab)
        {
            ActiveTab = tab;
            NotifyStateChanged();
            StateHasChanged();
        }
    }

    private void NotifyStateChanged() => TabChanged?.Invoke();

}

TabSet also uses Tab.razor: TabSet 也使用 Tab.razor:

@using Components.Tabs
@implements ITab

<li>
    <a @onclick="Activate" class="nav-link @TitleCssClass" role="button">
        @Title
    </a>
</li>

@code {
    [CascadingParameter]
    public TabSet ContainerTabSet { get; set; }

    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }


    private string TitleCssClass => ContainerTabSet.ActiveTab == this ? "active" : null;

    protected override void OnInitialized()
    {
        ContainerTabSet.AddTab(this);
    }

    private void Activate()
    {
        ContainerTabSet.SetActiveTab(this);
    }
}

And ITab.cs Interface和 ITab.cs 接口

using Microsoft.AspNetCore.Components;

namespace PlatformAdmin.Components.Tabs
{
    public interface ITab
    {
        RenderFragment ChildContent { get;  }

        public string Title { get; }
    }
}

It's taken from a Steve Sanderson example found here它取自史蒂夫·桑德森 (Steve Sanderson) 的示例,可在此处找到

EDIT 2编辑 2

Here is the debugger showing tabSet is null on first render:这是第一次渲染时显示 tabSet 为 null 的调试器:

在此处输入图像描述

And not null on additional renders:而不是 null 附加渲染:

在此处输入图像描述

As Dani Herrera pointed out in the comments this may be due to the component being withing an if/else statement and indeed it was.正如Dani Herrera在评论中指出的那样,这可能是由于组件带有 if/else 语句,确实如此。 Previously I had the component hidden if an object was null:以前,如果 object 是 null,我会隐藏该组件:

@if(Account != null)
{
    <TabSet @ref="tabSet">
     ...
    </TabSet>
}

I left this out for brevity and made the incorrect assumption that the issue was not the conditional.为了简洁起见,我忽略了这一点,并做出了错误的假设,即问题不是有条件的。 I was very wrong as on first render the object is null and therefore the component does not exist.我在第一次渲染时非常错误,object 是 null,因此该组件不存在。 So be careful out there: I resolved it by moving my conditionals to the sections within the component:所以要小心:我通过将条件移动到组件内的部分来解决它:

<TabSet @ref="tabSet">
    @if(Account != null)
    {
        <Tab>
         ...
        </Tab>
        <Tab>
         ...
        </Tab>
    }
</TabSet>

In first render, component is rendered.在第一次渲染中,组件被渲染。 After first render, referent object is refer to component.第一次渲染后,引用 object 是指组件。 So, at first time, ref of component is null(not ref).因此,第一次,组件的 ref 为空(不是 ref)。 For more detail, document of blazor is detail issue in here有关更多详细信息,blazor 文档是此处的详细问题

The tabSet variable is only populated after the component is rendered and its output includes the TabSet element. tabSet 变量仅在组件被渲染并且其 output 包含 TabSet 元素后才被填充。 Until that point, there's nothing to reference.在那之前,没有什么可以参考的。 To manipulate components references after the component has finished rendering, use the OnAfterRenderAsync or OnAfterRender methods.要在组件完成渲染后操作组件引用,请使用 OnAfterRenderAsync 或 OnAfterRender 方法。

in my case it's solved by initialize the object在我的情况下,它通过初始化 object 来解决

EX: private TabSet tabSet = new TabSet();例如:私有 TabSet tabSet = new TabSet();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM