Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
493 views
in Technique[技术] by (71.8m points)

Action passed to a Blazor not component doesn't update the parent until something else does

If I have a Blazor component called DoAction that consists of nothing more than this...

@code {
  public void DoItNow(Action action) =>
    action();
}

...and I use it in another component as follows...

<p>@((MarkupString)_msg)</p>
<br/><button @onclick="DoItNow">Do it now</button>
<DoAction @ref="_doAction" />

@code {
  private string _msg;
  private DoAction _doAction;

  private void DoItNow() =>
    _doAction.DoItNow(() => _msg += $"<br/>{DateTime.Now.ToLongTimeString()}: DoItNow");
}

...then it works fine. I can click the button and the display gets updated.

However, if I change this so that the DoAction shows a button and only performs the action when the button is clicked, it doesn't work, see below.

Updated DoAction component...

<button class="btn btn-primary" @onclick="ButtonClicked">Click me</button>

@code {

  private bool _showButton = false;
  private Action _action;

  public void DoItAfterButtonClick(Action action) {
    // Hold on to the action for use below
    _action = action;
    _showButton = true;
  }

  private void ButtonClicked() {
    _action();
    _showButton = false;
  }

}

Updated parent component...

<p>@((MarkupString)_msg)</p>

<br/><button @onclick="DoItAfterButtonClick">Do it after button click</button>
<DoAction @ref="_doAction" />

@code {
  private DoAction _doAction;

  private void DoItAfterButtonClick() =>
    _doAction.DoItAfterButtonClick(
      () => _msg += $"<br/>{DateTime.Now.ToLongTimeString()}: DoItAfterButtonClick");
}

...then when I click the button in the parent, the button in the DoAction component appears, but when I click that, nothing happens until something else updates the UI. At that point, I see the message that wasn't displayed when I clicked the DoAction component's button.

I tried adding a call to StateHasChanged() inside DoAction, but it didn't make any dfference. The only way I can get it to work is if I call StateHasChanged() in the parent component...

  private void DoItAfterButtonClick() =>
    _doAction.DoItAfterButtonClick(
      () => {
        _msg += $"<br/>{DateTime.Now.ToLongTimeString()}: DoItAfterButtonClick";
        StateHasChanged();
      });

Anyone any idea why the UI is not updated in the second case?

question from:https://stackoverflow.com/questions/65875949/action-passed-to-a-blazor-not-component-doesnt-update-the-parent-until-somethin

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Welcome...

I'm not sure I understand what you're trying to achieve. I'd suggest you won't do it now or ever...

Note: The Action delegate encapsulates a method (code) that is owned by the parent component, not the DoAction component, and it is only executed from the DoAction component

In the first version when you click on the "DoItNow" button, the delegate is invoked from the DoAction component, and the rendering process notifies the parent component that an event has occurred (the StateHasChanged method is called) and that it should re-render. Note that the StateHasChanged method is automatically called by the framework when UI events are involved, as in the current case of clicking on the "DoItNow" button

public void DoItNow(Action action) =>
    action();

the parent component is re-rendered, and thus the display gets updated.

In the second version, the Action delegate _action is invoked when the "ButtonClicked" button is clicked, the rendering process notifies the DoAction that an event has occurred (the StateHasChanged method is called; again a UI event) and that it should re-render. The DoAction component is of course re-rendered, and the "ButtonClicked" button, as you can see, is not rendered after setting: _showButton = false; But the parent component is not aware of this. The parent component is not aware of the changes it undergone as a result of the invocation of the Action delegate that encapsulates (method) code owned by it...

The solution of course is to add the StateHasChanged method like this:

_doAction.DoItAfterButtonClick(
    () => { _msg += $"<br/>{DateTime.Now.ToLongTimeString()}: DoItAfterButtonClick"; StateHasChanged(); }); 

Of course you have already found the solution... I hope now you've found the explanation as for why the UI is not updated in the second case...

Bonus:

Use EventCallback 'delegate' whenever possible... Use Component parameters... You can define an EventCallback component parameter in DoAction that when invoked execute code on the parent component without the need to call the StateHasChanged method, and without being baffled about what is going on here...


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...