Search code examples
asp.net-coreblazorblazor-client-side

using current item from foreach loop in component in code section


Quick question...

Is the following code "legal" in Blazor? I mean can I trust that the currentTag variable will always be right, or do I have to "lock" it someway? Not sure how else to get the current item from the loop and use it in the code.

I have a foreach loop on a component and then saving the current item to a variable in my code below. The variable is used immediately after to bind to another childcomponent


@if (DataTags != null)
{
    @foreach (var dataTag in DataTags)
    {
       // Set currentDataTag 
       @{currentDataTag = dataTag;
        }
          <TagValueForm @bind-TagKeyValuePair="TagKeyValuePair" DataTag="dataTag"></TagValueForm>
    }
}

@code {
    [Parameter]
    public ICollection<Tag> DataTags { get; set; }

    [Parameter]
    public IDictionary<string, string> Tags { get; set; } 

    // Use the current tag:
    private Tag currentDataTag;

    private KeyValuePair<string, string> _tagKeyValuePair;
    private KeyValuePair<string, string> TagKeyValuePair
    {
        get
        {
            if (currentDataTag != null) //would never be null.. or?
            {
                return new KeyValuePair<string, string>(Tags.Keys.Where(x => x == currentDataTag.Name).FirstOrDefault(), Tags[currentDataTag.Name]);
            }
            else
            {
                return _tagKeyValuePair;
            }
        }
        set
        {
            //Update tag dictionary
            _tagKeyValuePair = value;
            Tags[_tagKeyValuePair.Key] = _tagKeyValuePair.Value;
            TagsChanged.InvokeAsync(Tags);
        }
    }


}


protected async override Task OnParametersSetAsync()
    {
        if (Tags == null)
        {
            Tags = new Dictionary<string, string>();
        }

        if (DataTags != null)
        {
            Tags = DataTags.ToDictionary(x => x.Name, x => { return Tags.ContainsKey(x.Name) ? Tags[x.Name] : null; });
        }


        await base.OnParametersSetAsync();
    }

It seems to work with my tests so far, but unsure if I'm just lucky :) Could try to do some delays here and there to test more.


Solution

  • Your @foreach block is setting the currentDataTag field for every iteration, so in the end the value for the last iteration is where it stops. Your code is working, which is good, but now you have a field with a value that could misrepresent what you are trying to do later on.

    I'd probably remove this part:

    // Set currentDataTag 
    @{currentDataTag = dataTag;
    }
    

    and find a way to do everything that needed to be done originally with currentDataTag in the same loop iteration. If it's bound to another component, render that component also inside the loop. That way you can avoid any issues that could arise as a result of depending on state outside the loop, i.e. what if that value was changed from somewhere else, or read from at an unexpected time.

    As a last resort, at the end of each loop iteration, set that currentDataTag to null or a base value so you can't read stale or incorrect data later on. Post some more code for us for better context if this doesn't help.