event 를 사용한 선택적 Refresh

블레이저를 사용하다가 알게 된 팁들을 모아 놓은 슬로그입니다.

용어

요소 : 레이저 컴포넌트 (.razor)
페이지 : RouteAttribute (@page 마크업 포함)가 부여된 요소(.razor)
콘트롤 : RouteAttribute (@page 마크업 포함)가 부여되지 않은 요소(.razor),

블레이저 렌더링 특성

블레이저의 렌더 트리는 단 방향 데이터 흐름(Unidirectional Data Flow) 모델이라, 부모 요소에서 자식 요소로 데이터가 전달됩니다.

이는 부모의 상태 중 하나라도 변경되면 부모의 렌더 트리에 포함된 모든 자식 요소들도 re-rendering 됨을 의미합니다.

이러한 행태의 장점은 UI가 언제나 모든 요소의 모든 상태를 반영하게 된다는 점입니다.
실제로 매우 유용하고 렌더링 속도도 빨라 일반적으로 전혀 문제가 없습니다.

그러나, 자식 요소의 상태를 계산하는데 시간이 많이 걸리는 경우, 렌더링 지연 문제가 있을 수 있습니다.
특히, 아래와 같이 자식 요소의 갯수가 많다면, 잦은 리렌더링은 큰 문제가 됩니다.



이벤트 캐스케이딩

이러한 문제는

  • 상태의 관리를 가급적 자식 요소에게 위임하고,
  • 데이터 변화가 있는 요소만 리렌더링하도록 만들면

일정 부분 회피할 수 있습니다.

이를 위해 블레이저의 Cascading 요소와 이벤트 시스템을 사용할 수 있습니다.

// SpreadSheet.razor : 부모 요소

<CascadingValue Value ="@DataChanged">
   @foreach(var d in _data)
   {
      <Cell Data="@d" />
   } 
</CascadingValue>

@code{
    public List<Data> DataCache = new();
    private event EventHandler DataChanged = (o, e) => { };
    private List<Data> _dataStore = new();

    protected override bool ShouldRender() => false;

   // 처리가 필요한 데이터 임시 저장.
    private void CacheData(Data d) => DataCache.Add(d);

   // 임시 저장된 데이터의 처리 요구.
    private void ProcessCacheData() => DataChanged.Invoke(this, EventArgs.Empty);
}
// Cell.razor : 자식 요소

@code{
    [CascadingParameter]
    public EventHandler DataChanged { get; set; } = null!;

    [Parameter]
    public Data _myData { get; set;} = null!;

    protected override void OnParameterSet
    {
        if(DataChanged is null) return;
        DataChanged += OnDataChanged;
    }

    private void OnDataChanged(object? parent, EventArgs e)
    {
        if(parent != null && parent.DataCache.Contains(_myData))
        {
           // _myData 처리
           parent.DataCache.Remove(_myData);
           StateHasChanged();  
        }
    }
}
6개의 좋아요