Closed honeyhead closed 3 months ago
Hi, you shouldn't cache pages, why are doing that? I mean, why is it a problem to create pages at every render?
What I wanted was, when implementing a tab bar, although it doesn't matter for static profile pages that always look the same, for pages that load data from the network and then render a list, I wanted to avoid having the main layout hold all state or props and pass them during tab transitions.
I might not have fully understood mauireactor, but typically when implementing a tab bar, data fetched initially remains persistent across tab switches, allowing the user to refresh as needed. However, constantly creating new instances with new custompage()
each time felt burdensome. Instead, I wanted to instantiate the pages initially and refresh them only when necessary, similar to a singleton pattern.
From your comments, it seems I might not fully grasp the concept, but I’m concerned that if a WebView is on a specific page, switching tabs and returning would cause unintended behavior by re-rendering and losing the user’s place or state.
I wonder if it's possible to achieve what I mentioned by keeping the actual view, such as a WebView, as a state so that the user sees it as they left it. However, it seems like I might not fully understand the concept of state yet. I would appreciate any good advice
Just to chime in with a possible solution for this - maybe instead of storing the state inside the VIEW, store the state inside the MODEL - this way the state is decoupled from the VIEW and makes it easier for you to re-use this state - also moves the logic from the VIEW to the MODEL regarding the getting of the data which makes your VIEWs have a single-purpose: Displaying the state
Hey sorry for the late reply, as @ThaDaVos was saying you need to put your state in a poco class, just plain properties (not a visual node), and couple it from the views.
Before dealing with more complex scenarios I suggest starting with simple things like having a state that you share between 2 pages. For example:
class TestBug245State
{
public int TabIndex { get; set; }
public int GlobalConter { get; set; }
}
class TestBug245 : Component<TestBug245State>
{
public override VisualNode Render()
{
return ContentPage(
Grid("Auto,*", "*",
Grid("*","*,*",
Button("Tab 1", () => SetState(s => s.TabIndex = 0)),
Button("Tab 2", () => SetState(s => s.TabIndex = 1)).GridColumn(1)
),
RenderCurrentTab()
.GridRow(1)
)
);
}
Component RenderCurrentTab()
{
return State.TabIndex switch
{
0 => new TestBug245Page1()
.State(State.GlobalConter)
.NewState(newValue => SetState(s => s.GlobalConter = newValue)),
1 => new TestBug245Page2()
.State(State.GlobalConter)
.NewState(newValue => SetState(s => s.GlobalConter = newValue)),
_ => throw new NotImplementedException(),
};
}
}
partial class TestBug245Page1 : Component
{
[Prop]
int _state;
[Prop]
Action<int>? _newState;
public override VisualNode Render()
{
return Button($"({_state}) Add 1", () => _newState?.Invoke(_state + 1))
.Center();
}
}
partial class TestBug245Page2 : Component
{
[Prop]
int _state;
[Prop]
Action<int>? _newState;
public override VisualNode Render()
{
return Button($"({_state}) Double it", () => _newState?.Invoke(_state * 2))
.Center();
}
}
PS The above sample can be simplified using a context:
https://adospace.gitbook.io/mauireactor/components/component-parameters
I think the questioner's intent is asking for the cache component to be instantiated and available.
For example. You want to display your screen as a webview and the address of the webview is Google, If you search for “maui reactor” in the Google search engine, you'll see something like this
When I go to another screen and come back, I don't want to see the Google search engine, I want to see the last “MauiReactor” search screen.
Thank you for your answer. @adospace I think I asked the wrong question. It is already implemented that the subject with the tab sends a new value to the components in each tab.
Rather than that, I'll take one as an example. If there is a MainPage component with index 0 in the tab, it has WebView.
And after the web view draws the screen, when the user goes to another tab and returns to the tab with MainPage, it is unmouted, so the web view is redrawn as if it was refreshed from the beginning.
According to your sample, you can create a web page address or action changed in the 0th index from another tab or the navigation tab control itself
I can't call onMouted and Render() if I created a Component with Singleton or specified it as the collection I mentioned as an example and implemented it.
Maybe it becomes UnMouted when I go to another tab but I think I need the option to control this or the ability to allow the way I implement it and manually Render().
I'd be in trouble if there was a web view where the user moved scrolls or moved carousels or typed something in the input form but was refreshed every time because they went to another tab.
In that case, you can just show/hide the current page, the native control (webView) will be kept in memory maintaining the scroll position or the current URL:
class TestBug245_2State
{
public int TabIndex { get; set; }
}
class TestBug245_2 : Component<TestBug245_2State>
{
public override VisualNode Render()
{
return ContentPage(
Grid("Auto,*", "*",
Grid("*","*,*",
Button("Tab 1", () => SetState(s => s.TabIndex = 0)),
Button("Tab 2", () => SetState(s => s.TabIndex = 1)).GridColumn(1)
),
RenderCurrentTab()
.GridRow(1)
)
);
}
Grid RenderCurrentTab()
{
return Grid(
new TestBug245_2Page1()
.IsVisible(State.TabIndex == 0),
new TestBug245_2Page2()
.IsVisible(State.TabIndex == 1)
);
}
}
partial class TestBug245_2Page1 : Component
{
[Prop]
bool _isVisible;
public override VisualNode Render()
{
return WebView("https://www.google.com")
.IsVisible(_isVisible);
}
}
partial class TestBug245_2Page2 : Component
{
[Prop]
bool _isVisible;
public override VisualNode Render()
{
return Label("Other Page")
.Center()
.IsVisible(_isVisible);
;
}
}
If you, in any case, find in the need to cache views you're doing something wrong: MauiReactor is intended to recycle views keeping only states in memory between render passes.
Oh, it works. Thank you!
VisualNode RenderPage() => State.CurrentPage switch
{
PageEnum.Home => new Tabs.HomePage().IsVisible(State.CurrentPage == PageEnum.Home ? true : false),
PageEnum.HighFive => new Tabs.HighFivePage().IsVisible(State.CurrentPage == PageEnum.HighFive ? true : false),
PageEnum.Add => new Tabs.AddPage().IsVisible(State.CurrentPage == PageEnum.Add ? true : false),
PageEnum.Chat => new Tabs.ChatPage().IsVisible(State.CurrentPage == PageEnum.Chat ? true : false),
PageEnum.Profile => new Tabs.ProfilePage().IsVisible(State.CurrentPage == PageEnum.Profile ? true : false),
_ => throw new ArgumentOutOfRangeException()
};
This was my old code. I tried adding IsVisible, but it didn't work
Grid RenderPage()
{
return Grid(
new Tabs.HomePage()
.IsVisible(State.CurrentPage == PageEnum.Home ? true : false),
new Tabs.HighFivePage()
.IsVisible(State.CurrentPage == PageEnum.HighFive ? true : false),
new Tabs.AddPage()
.IsVisible(State.CurrentPage == PageEnum.Add ? true : false),
new Tabs.ChatPage()
.IsVisible(State.CurrentPage == PageEnum.Chat ? true : false),
new Tabs.ProfilePage()
.IsVisible(State.CurrentPage == PageEnum.Profile ? true : false)
);
}
I replaced it with your sample and it was working.
The return type is not VisualNode, but Grid is not replaced, so the contents seem to be maintained inside, but not in my case, can you tell me why it is the way you did it?
Well, your code is pretty different from mine. In your case, you are "switching" between pages using the switch clause, i.e. you are returning only one of the possible tabs (the new tab is created and the old one is deleted). In the below code, I create a Grid containing all the pages making visible only the current one (the other tabs are just hidden but not removed from the visual tree).
Thank you ! I see how it works.
I made BottomTab.
We are developing pages made of CreatePage() as RenderContent(), so that each page is initially called and the Index of Tab is changed every time OnTabSelected is called.
The problem is that new HomePage(), newResvStatePage() are created every time a tab is switched. I don't want pages created more than once to be created as instances again
So I created CachedPages and tried to return it, and I made HomePage and ResvStatePage directly inside the MainLayout class as instance variables and returned it, but the Render or Mount method was called on the first call, but I haven't been able to draw the screen at all since then because I haven't called the two methods.
How can I solve it?