adospace / reactorui-maui

MauiReactor is a MVU UI framework built on top of .NET MAUI
MIT License
568 stars 47 forks source link

Unable to load listview via async method #110

Closed MattePozzy closed 1 year ago

MattePozzy commented 1 year ago

Hi, I need to load the items of a listview usign an async method that load the data from a sqlite.

I have tried to load them in OnMounted method and OnAppearing of ContentPage but the items are not rendered.

This is the code used:

class MyItem
    {
        public string Name { get; set; }
    }

    class ListPageState
    {
        public List<MyItem> Items;
    }

    class ListPage : Component<ListPageState>
    {
        // tried here with no luck
        //protected override async void OnMounted()
        //{
        //    SetState(async s => s.Items = await LoadItems());
        //    base.OnMounted();
        //}

        public override VisualNode Render()
        {
            return new ContentPage("wo list")
            {
                new Grid("auto,*","*,auto")
                {
                    new SearchBar().GridRow(0).GridColumn(0).Text(() => State.Items?.Count.ToString()),
                    new Button().GridRow(0).GridColumn(1).Margin(new Thickness(-1,-1,5,-1))
                        .BackgroundColor(Colors.Transparent).TextColor(Colors.Blue)
                        .VerticalOptions(MauiControls.LayoutOptions.Center)
                        .HorizontalOptions(MauiControls.LayoutOptions.Center)
                        .WidthRequest(50).Text("F"),
                    new ListView(MauiControls.ListViewCachingStrategy.RecycleElementAndDataTemplate).GridRow(1).GridColumn(0).GridColumnSpan(2)
                        .SelectionMode(MauiControls.ListViewSelectionMode.Single)
                        .ItemsSource(State.Items, ItemTemplate)
                }
            }.OnAppearing(() => SetState(async s => s.Items = await LoadItems()));
        }

        private async Task<List<MyItem>> LoadItems()
        {
            // simulate DB reading
            await Task.Delay(500);

            return new List<MyItem> {
                new MyItem {Name = "111111" },
                new MyItem {Name = "222222" },
                new MyItem {Name = "333333" },
                new MyItem {Name = "444444" },
            };
        }

        private ViewCell ItemTemplate(MyItem item)
        {
            return new ViewCell
            {
                new Button()
                    .BackgroundColor(Colors.Red)
                    .Text(item.Name)
                    .TextColor(Colors.Black)
                    .OnClicked(() =>
                    {
                        //menuAction.Action.Invoke();
                        //HideActionMenuOverlay();
                    })
            };
        }
    }

attached a sample.

MauiTest.zip

Thank you.

Code-DJ commented 1 year ago

You need to use SetState.

       protected override async void OnMounted()
       {
          var items = await LoadItems();
          SetState(state => state.Items = items);
          base.OnMounted();
       }
MattePozzy commented 1 year ago

You need to use SetState.

       protected override async void OnMounted()
       {
          var items = await LoadItems();
          SetState(state => state.Items = items);
          base.OnMounted();
       }

Thank you for the reply! I do SetState in OnAppearing of the ContentPage. I also have tried to do it in OnMounted but doesn't work (sorry but the code I have posted is outdated, I fix it now).

Code-DJ commented 1 year ago

You need to keep the async/await at the OnMounted level.

works:

       protected override async void OnMounted()
       {
          var items = await LoadItems();
          SetState(state => state.Items = items);
          base.OnMounted();
       }

vs. (doesn't work):

       protected override void OnMounted()
       {
          SetState(async s => s.Items = await LoadItems());
          base.OnMounted();
       }

At least works on iOS for me. If you don't want to async await in OnMounted, you will have to use Task.Run and put SetState inside it.

MattePozzy commented 1 year ago

You need to keep the async/await at the OnMounted level.

works:

       protected override async void OnMounted()
       {
          var items = await LoadItems();
          SetState(state => state.Items = items);
          base.OnMounted();
       }

vs. (doesn't work):

       protected override void OnMounted()
       {
          SetState(async s => s.Items = await LoadItems());
          base.OnMounted();
       }

At least works on iOS for me. If you don't want to async await in OnMounted, you will have to use Task.Run and put SetState inside it.

Thank you, in this way works! I have load the items in the OnAppearing method of the ContentPage, and works also there.

Thank you!