useQueries 失去了并行查询数据的能力,因为 Next.js 要求服务器组件异步
该应用程序由 Next.js 运行。我有一个 useQueries 钩子:const userQueries = useQueries({ queries: user.contacts.map((contactId: string) => ({ queryKey: ['contact', conta...
该应用程序由 Next.js 运行。
我有一个 useQueries 钩子:
const userQueries = useQueries({
queries: user.contacts.map((contactId: string) => ({
queryKey: ['contact', contactId],
queryFn: () => getUserById(contactId),
}))
});
api.ts 文件中的函数“getUserById”:
export async function getUserById(userId: string) {
const supabase = createClient();
let {data} = await supabase
.from('users')
.select()
.eq('id', userId);
return data && data[0]
}
我发现当我以这种方式加载数据时,它会按顺序加载而不是并行加载。
我尝试将 api 函数设为非异步,以便每个请求不会等待前一个请求,但 Next.js 禁止在没有“async”前缀的情况下执行服务器操作。有办法解决这个问题吗?
下载声明: 本站所有软件和资料均为软件作者提供或网友推荐发布而来,仅供学习和研究使用,不得用于任何商业用途。如本站不慎侵犯你的版权请联系我,我将及时处理,并撤下相关内容!
-
我正在开发一款专用于 Windows 的 MAUI C# 应用程序。这款应用程序的独特之处在于它与本地数据库配合使用,并调用填充服务器上数据库的 API。在此应用程序中,我有一个 p...
我正在开发一款专用于 Windows 的 MAUI C# 应用程序。这款应用程序的独特之处在于它与本地数据库配合使用,并调用一个 API 来填充服务器上的数据库。在此应用程序中,我有一个显示项目列表的页面。在此页面上,我还有一些按钮,允许我添加/更新/删除此列表中的项目。这些按钮会调出一个用于更新或添加项目的模式。
当我单击其中一个按钮时,模态框出现,然后我进行更改。由于模态框的背景是透明的,我可以看到修改已应用于列表。但是,模态框不会像代码中预期的那样自动关闭。我必须单击“退出”才能使模态框消失。
返回列表后,如果我想再次与列表交互,我会单击其中一个按钮。模式打开,我执行操作(添加/更新),我看到修改正在进行,但模式没有关闭并冻结。如果我单击“退出”,我会收到此错误:Application.Current.MainPage.Navigation.ModalStack.Count = 0,并且应用程序崩溃。
如果我将按钮中的所有逻辑都注释掉,我也会遇到同样的问题。
我尝试了很多方法,但我觉得我在主线程管理中明显缺少了一些东西。我不太了解 MAUI 中的线程。有人能帮我吗?
提前致谢。
这是我的代码,其中我的列表的视图
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="FntrAudit.Views.RequiredDisplayPage" Title="Affichage Obligatoire"> <Grid RowDefinitions="Auto,*,Auto" ColumnDefinitions="*,*,*"> <Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Text="Affichages obligatoires" FontSize="Large"></Label> <ListView Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" ItemsSource="{Binding RequiredDisplays}"> <ListView.ItemTemplate> <DataTemplate > <ViewCell> <StackLayout Orientation="Horizontal" VerticalOptions="Center" Spacing="10"> <CheckBox IsChecked="{Binding IsOk}" VerticalOptions="Center"/> <Label Text="{Binding Intitule}" VerticalOptions="Center" HorizontalOptions="FillAndExpand" LineBreakMode="WordWrap"/> <ImageButton Clicked="UpdateDoc" HeightRequest="20" WidthRequest="20" Source="update.png" CommandParameter="{Binding .}"></ImageButton> <ImageButton Clicked="Delete_Clicked" HeightRequest="20" WidthRequest="20" Source="delete.png" CommandParameter="{Binding .}"></ImageButton> </StackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> <Button BackgroundColor="#AE1439" Grid.Row="2" Grid.Column="1" Text="Enregistrer et revenir à l'audit" Margin="5" Clicked="Record_Clicked" HorizontalOptions="Fill"></Button> <Button BackgroundColor="#AE1439" Grid.Row="2" Grid.Column="0" Text="Ajouter document obligatoire" Margin="5" Clicked="AddDoc" HorizontalOptions="Fill"></Button> </Grid> </ContentPage>
背后的代码
using FntrAudit.DALApi; using FntrAudit.Data; using FntrAudit.Models; using FntrAudit.Utils; using FntrAudit.Views.Modal; using Microsoft.EntityFrameworkCore; using System.Collections.ObjectModel; namespace FntrAudit.Views; public partial class RequiredDisplayPage : ContentPage { private Audit _audit; SqliteDbContext _db = new SqliteDbContext(); private ObservableCollection<RequiredDisplay> requiredDisplays; private RestService<RequiredDisplay> _requiredDisplayService = new RestService<RequiredDisplay>(); private RestService<LogFromApp> _LogService = new RestService<LogFromApp>(); public ObservableCollection<RequiredDisplay> RequiredDisplays { get { return requiredDisplays; } set { if (requiredDisplays != value) { requiredDisplays = value; OnPropertyChanged(nameof(RequiredDisplays)); } } } public RequiredDisplayPage(Audit audit) { InitializeComponent(); _audit = audit; InitializeAsync(); } private async void InitializeAsync() { await LoadRequiredDoc(); } public async Task LoadRequiredDoc() { try { Console.WriteLine("Loading required documents..."); bool hasWeb = Connectivity.Current.NetworkAccess == NetworkAccess.Internet; List<RequiredDisplay> requiredDisplays = new List<RequiredDisplay>(); if (_audit.RequiredDisplay.Any(rd => rd.IsOk)) { RequiredDisplays = new ObservableCollection<RequiredDisplay>(_audit.RequiredDisplay); } else { RequiredDisplays = new ObservableCollection<RequiredDisplay>(_audit.RequiredDisplay); } await Task.Delay(1000); this.BindingContext = this; Console.WriteLine("Required documents loaded successfully."); } catch(Exception ex) { var t = ex.Message; Console.WriteLine($"Error loading required documents: {ex.Message}"); } } private async void Delete_Clicked(object sender, EventArgs e) { try { bool answer = await DisplayAlert("Supprimer un document obligatoire", "Êtes-vous sûr de vouloir supprimer ce document ?", "Oui", "Non"); if (answer) { if (sender is ImageButton button && button.CommandParameter is RequiredDisplay toDelete) { try { Console.WriteLine($"Attempting to delete document: {toDelete.GuidRDisplay}"); _db.RequiredDisplay.Remove(toDelete); await _db.SaveChangesAsync(); await _requiredDisplayService.DeleteTodoItemAsync(toDelete.GuidRDisplay, Constantes.URLAPI + "RequiredDisplay/"); RequiredDisplays.Remove(toDelete); Console.WriteLine($"Document {toDelete.GuidRDisplay} deleted successfully."); } catch (DbUpdateConcurrencyException) { await _db.Entry(toDelete).ReloadAsync(); await DisplayAlert("Échec de la suppression", "Le document a été modifié ou supprimé par une autre transaction. Veuillez réessayer.", "OK"); Console.WriteLine($"Concurrency error while deleting document: {toDelete.GuidRDisplay}"); } catch (Exception ex) { LogFromApp logFromApp = new LogFromApp() { DateCreation = DateTime.Now, Intitule = "ModalRequired delete doc (UserContext ContextCurrent) " + ex.Message, Trace = ex.ToString(), UserInvolved = " " }; await _LogService.Create(logFromApp, Constantes.URLAPILOG, true); await DisplayAlert("Erreur", "Une erreur est survenue lors de la suppression du document.", "OK"); Console.WriteLine($"Error deleting document: {ex.Message}"); } } } } catch (Exception ex) { await DisplayAlert("Erreur", "Une erreur est survenue lors de la suppression du document.", "OK"); Console.WriteLine("Erreur lors de la suppression du document : " + ex.Message); } } //public async void Delete_Clicked(object sender, EventArgs e) //{ // bool answer = false; // try // { // answer = await DisplayAlert("Supprimer un document obligatoire", "Êtes-vous sûr de vouloir supprimer ce document ?", "Oui", "Non"); // } // catch(Exception ex) // { // var toto = ex.Message; // } // if (answer) // { // if (sender is ImageButton button && button.CommandParameter is RequiredDisplay toDelete) // { // try // { // _db.RequiredDisplay.Remove(toDelete); // _db.SaveChanges(); // await _requiredDisplayService.DeleteTodoItemAsync(toDelete.GuidRDisplay, Constantes.URLAPI + "RequiredDisplay/"); // RequiredDisplays.Remove(toDelete); // } // catch (DbUpdateConcurrencyException ex) // { // // Option 1: Raffraîchir l'entité depuis la base de données // await _db.Entry(toDelete).ReloadAsync(); // // Afficher un message d'erreur à l'utilisateur // await DisplayAlert("Échec de la suppression", "Le document a été modifié ou supprimé par une autre transaction. Veuillez réessayer.", "OK"); // // Option 2: Tentative de réexécution de la suppression après rechargement // // _db.RequiredDisplay.Remove(toDelete); // // _db.SaveChanges(); // } // catch (Exception ex) // { // // Afficher un message d'erreur générique // await DisplayAlert("Erreur", "Une erreur est survenue lors de la suppression du document.", "OK"); // } // } // } //} public async void Record_Clicked(object sender, EventArgs e) { try { Console.WriteLine("Recording clicked."); _audit.RequiredDisplay = RequiredDisplays.ToList(); await Application.Current.MainPage.Navigation.PopModalAsync(); Console.WriteLine("Modal closed successfully."); } catch (Exception ex) { Console.WriteLine($"Error in Record_Clicked: {ex.Message}"); } } public async void AddDoc(object sender, EventArgs e) { try { RequiredDisplay requiredDisplay = new RequiredDisplay(); var modal = new ModalRequiredDisplay(requiredDisplay, true); modal.RequiredDisplays = this.RequiredDisplays; // await Application.Current.MainPage.Navigation.PushModalAsync(modal); var tcs = new TaskCompletionSource<bool>(); modal.Disappearing += async (s, args) => { Console.WriteLine("Modal disappearing..."); if (!tcs.Task.IsCompleted) { tcs.SetResult(true); } }; await Application.Current.MainPage.Navigation.PushModalAsync(modal); await tcs.Task; // Wait for the modal to close await LoadRequiredDoc(); } catch(Exception ex) { Console.WriteLine($"Error in AddDoc: {ex.Message}"); } } public async void UpdateDoc(object sender, EventArgs e) { try { if (sender is ImageButton button && button.CommandParameter is RequiredDisplay toUpdate) { var modal = new ModalRequiredDisplay(toUpdate, false); modal.RequiredDisplays = this.RequiredDisplays; var tcs = new TaskCompletionSource<bool>(); modal.Disappearing += (s, args) => { Console.WriteLine("Modal disappearing..."); if (!tcs.Task.IsCompleted) { tcs.SetResult(true); } }; await Application.Current.MainPage.Navigation.PushModalAsync(modal); Console.WriteLine("Modal pushed to stack."); await tcs.Task; // Wait for the modal to close Console.WriteLine("Modal closed."); await LoadRequiredDoc(); } }catch(Exception ex) { var t = ex.Message; } } }
情态动词
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="FntrAudit.Views.Modal.ModalRequiredDisplay" BackgroundColor="#00000000" Title="Affichage Obligatoire"> <ContentView HorizontalOptions="Center" VerticalOptions="Center"> <ScrollView> <Frame BackgroundColor="White" CornerRadius="20" Padding="20" HorizontalOptions="Center" VerticalOptions="Center" WidthRequest="700" HeightRequest="600"> <VerticalStackLayout Spacing="15"> <Label Text="{Binding TitleModal}" TextColor="Black" FontSize="24" FontAttributes="Bold" HorizontalOptions="Center"></Label> <Editor Text="{Binding Intitule}" Placeholder="Intitulé du document" AutoSize="TextChanges"></Editor> <VerticalStackLayout Spacing="10"> <Label Text="Effectif de l'entreprise concernée (si besoin)" VerticalOptions="CenterAndExpand" FontAttributes="Bold"/> <VerticalStackLayout Spacing="5"> <Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto" Padding="5"> <Label Text="Toutes" Grid.Column="0" VerticalOptions="CenterAndExpand" Margin="0,0,10,0"/> <RadioButton x:Name="All" GroupName="Effectif" IsChecked="True" Grid.Column="2" VerticalOptions="CenterAndExpand"/> </Grid> <Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto" Padding="5"> <Label Text="de 1 à 10" Grid.Column="0" VerticalOptions="CenterAndExpand" Margin="0,0,10,0"/> <RadioButton x:Name="Has1SalOrMore" GroupName="Effectif" IsChecked="{Binding Has1SalOrMore}" Grid.Column="2" VerticalOptions="CenterAndExpand"/> </Grid> <Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto" Padding="5"> <Label Text="de 11 à 50" Grid.Column="0" VerticalOptions="CenterAndExpand" Margin="0,0,10,0"/> <RadioButton x:Name="Has11SalOrMore" GroupName="Effectif" IsChecked="{Binding Has11SalOrMore}" Grid.Column="2" VerticalOptions="CenterAndExpand"/> </Grid> <Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto" Padding="5"> <Label Text="de 51 à 300" Grid.Column="0" VerticalOptions="CenterAndExpand" Margin="0,0,10,0"/> <RadioButton x:Name="Has50SalOrMore" GroupName="Effectif" IsChecked="{Binding Has50SalOrMore}" Grid.Column="2" VerticalOptions="CenterAndExpand"/> </Grid> <Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto" Padding="5"> <Label Text="de 301 à 1000" Grid.Column="0" VerticalOptions="CenterAndExpand" Margin="0,0,10,0"/> <RadioButton x:Name="Has300SalOrMore" GroupName="Effectif" IsChecked="{Binding Has300SalOrMore}" Grid.Column="2" VerticalOptions="CenterAndExpand"/> </Grid> <Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto" Padding="5"> <Label Text="1000 et plus" Grid.Column="0" VerticalOptions="CenterAndExpand" Margin="0,0,10,0"/> <RadioButton x:Name="Has1000SalOrMore" GroupName="Effectif" IsChecked="{Binding Has1000SalOrMore}" Grid.Column="2" VerticalOptions="CenterAndExpand"/> </Grid> </VerticalStackLayout> </VerticalStackLayout> <HorizontalStackLayout Spacing="10"> <Button BackgroundColor="#007AFF" TextColor="White" Text="Enregistrer" Margin="5" Clicked="AddDoc_Clicked" HorizontalOptions="FillAndExpand"></Button> <Button BackgroundColor="#FF3B30" TextColor="White" Text="Quitter" Margin="5" Clicked="Quit_Clicked" HorizontalOptions="FillAndExpand"></Button> </HorizontalStackLayout> </VerticalStackLayout> </Frame> </ScrollView> </ContentView> </ContentPage>
背后的代码
using CommunityToolkit.Maui.Alerts; using FntrAudit.DALApi; using FntrAudit.Data; using FntrAudit.Models; using FntrAudit.Services; using FntrAudit.Utils; using Microsoft.EntityFrameworkCore; using System.Collections.ObjectModel; namespace FntrAudit.Views.Modal; public partial class ModalRequiredDisplay : ContentPage { private RequiredDisplay _document; private bool _isCreation; SqliteDbContext _db = new SqliteDbContext(); private ObservableCollection<RequiredDisplay> requiredDisplays; private RestService<RequiredDisplay> _requiredDisplayService = new RestService<RequiredDisplay>(); private RestService<LogFromApp> _LogService = new RestService<LogFromApp>(); public ObservableCollection<RequiredDisplay> RequiredDisplays { get { return requiredDisplays; } set { if (requiredDisplays != value) { requiredDisplays = value; OnPropertyChanged(nameof(RequiredDisplays)); } } } public ModalRequiredDisplay(RequiredDisplay document, bool isCreation) { InitializeComponent(); _document = document; _isCreation = isCreation; _document.TitleModal = _isCreation ? "Ajout d'un document obligatoire" : "Mise à jour d'un document obligatoire"; this.BindingContext = _document; } private async void Delete_Clicked(object sender, EventArgs e) { await CloseModalAsync(); } private async void Quit_Clicked(object sender, EventArgs e) { await CloseModalAsync(); } private async Task AddDoc(object sender, EventArgs e) { bool hasWeb = Connectivity.Current.NetworkAccess == NetworkAccess.Internet; try { if (_isCreation) { RequiredDisplay required = new RequiredDisplay(); _document.IsOk = false; _document.AuditId = 1; _document.GuidRDisplay = Guid.NewGuid().ToString(); await _db.RequiredDisplay.AddAsync(_document); await _db.SaveChangesAsync(); Func<Task> saveDocument = async () => { await _requiredDisplayService.Create(_document, Constantes.URLAPI + "RequiredDisplay", true); }; if (hasWeb) { await saveDocument.Invoke(); } else { var connectivityService = new ConnectivityService(); connectivityService.AddTask(saveDocument); } } else { _db.Update(_document); await _db.SaveChangesAsync(); Func<Task> updateDocument = async () => { await _requiredDisplayService.Create(_document, Constantes.URLAPI + "RequiredDisplay/RequiredDisplay/" + _document.GuidRDisplay, false); }; if (hasWeb) { await updateDocument.Invoke(); } else { var connectivityService = new ConnectivityService(); connectivityService.AddTask(updateDocument); } } await ShowToasterAsync("Document obligatoire enregistré avec succès."); } catch (Exception ex) { LogFromApp logFromApp = new LogFromApp() { DateCreation = DateTime.Now, Intitule = "ModalRequired (UserContext ContextCurrent) " + ex.Message, Trace = ex.ToString(), UserInvolved = " " }; await _LogService.Create(logFromApp, Constantes.URLAPILOG, true); } finally { try { if (RequiredDisplays == null) { RequiredDisplays = new ObservableCollection<RequiredDisplay>(); } RequiredDisplay requiredDisplay = null; if (RequiredDisplays.Count > 0 && _document != null) { requiredDisplay = RequiredDisplays.FirstOrDefault(rd => rd.GuidRDisplay == _document.GuidRDisplay); } if (requiredDisplay != null) { int index = RequiredDisplays.IndexOf(requiredDisplay); if (index != -1) { RequiredDisplays[index] = _document; } } await CloseModalAsync(); } catch (Exception closeEx) { Console.WriteLine("Erreur lors de la fermeture de la modal : " + closeEx.Message); } } } private async Task CloseModalAsync() { try { if (Application.Current.MainPage.Navigation.ModalStack.Count > 0) { await Application.Current.MainPage.Navigation.PopModalAsync(); Console.WriteLine("Modal closed successfully."); } else { Console.WriteLine("Modal stack is empty."); } } catch (Exception ex) { Console.WriteLine($"Error in CloseModalAsync: {ex.Message}"); } } private async void DeleteDoc(object sender, EventArgs e) { bool answer = await DisplayAlert("Supprimer un document obligatoire", "Êtes-vous sûr de vouloir supprimer ce document ?", "Oui", "Non"); if (answer) { if (sender is ImageButton button && button.CommandParameter is RequiredDisplay toDelete) { try { _db.RequiredDisplay.Remove(toDelete); await _db.SaveChangesAsync(); await _requiredDisplayService.DeleteTodoItemAsync(toDelete.GuidRDisplay, Constantes.URLAPI + "RequiredDisplay/"); RequiredDisplays.Remove(toDelete); } catch (DbUpdateConcurrencyException ex) { // Option 1: Raffraîchir l'entité depuis la base de données await _db.Entry(toDelete).ReloadAsync(); // Afficher un message d'erreur à l'utilisateur await DisplayAlert("Échec de la suppression", "Le document a été modifié ou supprimé par une autre transaction. Veuillez réessayer.", "OK"); // Option 2: Tentative de réexécution de la suppression après rechargement // _db.RequiredDisplay.Remove(toDelete); // _db.SaveChanges(); } catch (Exception ex) { LogFromApp logFromApp = new LogFromApp() { DateCreation = DateTime.Now, Intitule = "ModalRequired delete doc (UserContext ContextCurrent) " + ex.Message, Trace = ex.ToString(), UserInvolved = " " }; await _LogService.Create(logFromApp, Constantes.URLAPILOG, true); // Afficher un message d'erreur générique await DisplayAlert("Erreur", "Une erreur est survenue lors de la suppression du document.", "OK"); } } } } private async Task ShowToasterAsync(string message) { var toast = Toast.Make(message); await toast.Show(); } public async void AddDoc_Clicked(object sender, EventArgs e) { await AddDoc(sender, e); } }