Reputation: 9352
I have a blog website where comments are managed inside a Component
called CommentList.
index.razor
...
Counting comments: @CommentCount
<CommentList PostId="@CurrentPost.Id" OnCommentCountChanged="OnCommentCountChangedHandler" />
@code {
...
int CommentCount;
public void OnCommentCountChangedHandler(int count)
{
CommentCount = count;
}
}
And now the component:
CommentList.razor
[Parameter] public int PostId { get; set; }
[Parameter] public EventCallback<int> OnCommentCountChanged { get; set; }
[Inject] ICommentService CommentService { get; set; }
List<Comment> AllComments { get; set; }
bool flag;
protected override async Task OnParametersSetAsync()
{
if (PostId && !flag)
{
flag = true;
AllComments = await CommentService.GetComments(PostId);
await CountComments();
}
}
protected async Task CountComments()
{
Console.WriteLine("CountComments called");
if (AllComments == null) return;
var count = AllComments.Count();
await OnCommentCountChanged.InvokeAsync(count);
}
As you can see on the code above, in my component, I retrieved all the comments for this post though a service then I invoke a method to inform the parent of the count.
I needed to communicate the count to the parent as soon as I retrieved the list of comments. The only place I found for recomputing this count (CountingComments) was in OnParametersSetAsync
.
You'll notice that I used a flag. Without this flag, there is a never ending loop:
CountingComments
from OnParametersSetAsync
OnCommentCountChanged
from CountingComments
OnParametersSetAsync
With the flag in place the loop is avoided but I wonder if this is the best approach ?
Unfortunately it is not possible to make the distinction between each parameter changes. If we have 2 or 3 parameters, if one of these parameters changed, the method OnParametersSetAsync
is triggered but we don't have any idea which parameter is concerned. Furthermore an invoke to the EventCallback Parameter
OnCommentCountChanged also trigger this OnParametersSetAsync
with no interest (in my case).
Upvotes: 2
Views: 1005
Reputation: 45734
The code in the OnParametersSetAsync method retrieve the comments superfluously at each execution of the method. Instead you should use OnInitalized(Async) pair of methods to retrieve the data only once, when your CommentList component is created.
Now with that design, only when a new comment is added to the current post (of the blog, mind you), you'll need to refresh the AllComments
list by calling the CommentService.GetComments method again. How to call, when to call, etc. is all depending on your design.
Let me describe here an easy and workable design. Say at the bottom of your blog post is a comment section where you can enter a comment, and then click on a "Save my comment" button. You can define a service or Razor component to post the added comment to the relevant database table(s). This service or component should also have code to invoke an event that notify your CommentList of this occurrence (new comment added). In response, your code should call the CommentService.GetComments method to retrieve a fresh set of comments from the database.
Unfortunately it is not possible to make the distinction between each parameter changes
Why not ?
This is how you should define your [Parameter] public int PostId { get; set; }
[Parameter] public int PostId { get; set; }
private int _internalPostId;
private int InternalPostId
{
get => _internalPostId;
set
{
if(_internalPostId != value)
{
_internalPostId = value;
// Can only be true if a new post is displayed or selected
// in the Index component, which requires you to retrieve the
// comments for the new post id passed. Put here code that
// call the method that perform the database access to
// retrieve the comments. That should be the same code that
// you should have in OnInitalized(Async) pair of methods. So
// you can create a single method that should be called from
// OnInitalized(Async), and from here.
// Understand that OnInitalized(Async) is called only once,
// when the component is created... When the PostId parameter
// changes, the Component is not created again, but re-used.
// Note: If you define an asynchronous method that contain the
// code to retrieve the comments, you can still call it from
// here by applying the so-called fire-and-forget operation,
// as for instance: _ = MyService.GetSomething();
}
}
}
Note that I've introduced a new private property called InternalPostId
. It is used because parameter properties should not be mutated by the child component... They should be treated as auto properties. Hope you're not confused.
You'll also need to override the OnParametersSet method in order to update the internal property InternalPostId from the PostId parameter property.
protected override void OnParametersSet()
{
if (InternalPostId != PostId)
{
InternalPostId = PostId;
}
}
Upvotes: 2