feat: use function hooks instead of class hooks

This commit is contained in:
Kingkor Roy Tirtho
2023-02-26 10:51:28 +06:00
parent d964216ee1
commit 1ea9370570
8 changed files with 140 additions and 578 deletions
@@ -110,6 +110,7 @@ class QueryClient {
)
.cast<DataType, ErrorType, PageType>();
query.updateQueryFn(queryFn);
query.updateNextPageFn(nextPage);
cache.addInfiniteQuery(query);
return query;
}
@@ -314,6 +314,7 @@ class InfiniteQuery<DataType, ErrorType, PageType>
}
void updateNextPageFn(InfiniteQueryNextPage<DataType, PageType> nextPage) {
if (state._nextPage == nextPage) return;
state = state.copyWith(nextPage: nextPage);
}
@@ -5,10 +5,8 @@
import FlutterMacOS
import Foundation
import connectivity_plus_macos
import path_provider_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
}
@@ -6,9 +6,6 @@
#include "generated_plugin_registrant.h"
#include <connectivity_plus_windows/connectivity_plus_windows_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
}
@@ -3,7 +3,6 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
@@ -1,8 +1,9 @@
import 'dart:async';
import 'package:fl_query/fl_query.dart';
import 'package:fl_query_hooks/src/use_query_client.dart';
import 'package:fl_query_hooks/src/utils/use_updater.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/diagnostics.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
InfiniteQuery<DataType, ErrorType, PageType>
@@ -19,202 +20,60 @@ InfiniteQuery<DataType, ErrorType, PageType>
bool enabled = true,
List<Object?>? keys,
}) {
return use(UseInfiniteQuery(
final rebuild = useUpdater();
final client = useQueryClient();
final query = useMemoized(() {
return client.createInfiniteQuery<DataType, ErrorType, PageType>(
queryKey,
queryFn,
initialParam: initialPage,
nextPage: nextPage,
initialPage: initialPage,
retryConfig: retryConfig,
refreshConfig: refreshConfig,
jsonConfig: jsonConfig,
onData: onData,
onError: onError,
enabled: enabled,
keys: keys,
));
}
refreshConfig: refreshConfig,
retryConfig: retryConfig,
);
}, [client, queryKey]);
class UseInfiniteQuery<DataType, ErrorType, PageType>
extends Hook<InfiniteQuery<DataType, ErrorType, PageType>> {
final InfiniteQueryFn<DataType, PageType> queryFn;
final String queryKey;
useEffect(() {
return query.addListener(rebuild);
}, [query]);
final PageType initialPage;
final InfiniteQueryNextPage<DataType, PageType> nextPage;
final RetryConfig retryConfig;
final RefreshConfig refreshConfig;
final JsonConfig<DataType>? jsonConfig;
final ValueChanged<PageEvent<DataType, PageType>>? onData;
final ValueChanged<PageEvent<ErrorType, PageType>>? onError;
// widget specific
final bool enabled;
const UseInfiniteQuery(
this.queryKey,
this.queryFn, {
required this.nextPage,
required this.initialPage,
this.retryConfig = DefaultConstants.retryConfig,
this.refreshConfig = DefaultConstants.refreshConfig,
this.jsonConfig,
this.onData,
this.onError,
this.enabled = true,
super.keys,
}) : assert(
(jsonConfig != null && enabled) || jsonConfig == null,
'jsonConfig is only supported when enabled is true',
useEffect(
() {
if (enabled) {
query.fetch();
}
return null;
},
[enabled],
);
@override
createState() => _UseInfiniteQueryState<DataType, ErrorType, PageType>();
}
useEffect(() {
query.updateQueryFn(queryFn);
return null;
}, [queryFn, query]);
class _UseInfiniteQueryState<DataType, ErrorType, PageType> extends HookState<
InfiniteQuery<DataType, ErrorType, PageType>,
UseInfiniteQuery<DataType, ErrorType, PageType>> {
InfiniteQuery<DataType, ErrorType, PageType>? query;
VoidCallback? removeListener;
useEffect(() {
query.updateNextPageFn(nextPage);
return null;
}, [nextPage, query]);
useEffect(() {
StreamSubscription<PageEvent<DataType, PageType>>? dataSubscription;
StreamSubscription<PageEvent<ErrorType, PageType>>? errorSubscription;
void rebuild([_]) {
setState(() {});
if (onData != null) {
dataSubscription = query.dataStream.listen(onData);
}
if (onError != null) {
errorSubscription = query.errorStream.listen(onError);
}
Future<void> initialize() async {
setState(() {
_createQuery();
if (hook.onData != null)
dataSubscription = query!.dataStream.listen(hook.onData);
if (hook.onError != null)
errorSubscription = query!.errorStream.listen(hook.onError);
removeListener = query!.addListener(rebuild);
});
if (hook.enabled) {
await query!.fetch();
}
}
void _createQuery() {
query = QueryClient.of(context).createInfiniteQuery(
hook.queryKey,
hook.queryFn,
initialParam: hook.initialPage,
nextPage: hook.nextPage,
retryConfig: hook.retryConfig,
refreshConfig: hook.refreshConfig,
jsonConfig: hook.jsonConfig,
);
}
@override
void initHook() {
super.initHook();
WidgetsBinding.instance.addPostFrameCallback((_) {
initialize();
});
}
@override
void dispose() {
return () {
dataSubscription?.cancel();
errorSubscription?.cancel();
removeListener?.call();
super.dispose();
}
};
}, [onData, onError, query]);
@override
void didUpdateHook(oldHook) {
super.didUpdateHook(oldHook);
if (oldHook.queryKey != hook.queryKey) {
dataSubscription?.cancel();
errorSubscription?.cancel();
removeListener?.call();
initialize();
return;
} else if (oldHook.enabled != hook.enabled && hook.enabled) {
query!.fetch();
}
if (oldHook.queryFn != hook.queryFn) {
query!.updateQueryFn(hook.queryFn);
}
if (oldHook.nextPage != hook.nextPage) {
query!.updateNextPageFn(hook.nextPage);
}
if (oldHook.onData != hook.onData) {
dataSubscription?.cancel();
if (hook.onData != null)
dataSubscription = query!.dataStream.listen(hook.onData);
}
if (oldHook.onError != hook.onError) {
errorSubscription?.cancel();
if (hook.onError != null)
errorSubscription = query!.errorStream.listen(hook.onError);
}
}
@override
build(BuildContext context) {
if (query == null) {
_createQuery();
}
return query!;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<InfiniteQueryFn<DataType, PageType>>(
'queryFn',
hook.queryFn,
));
properties
.add(DiagnosticsProperty<InfiniteQueryNextPage<DataType, PageType>>(
'nextPage',
hook.nextPage,
));
properties.add(DiagnosticsProperty<PageType>(
'initialPage',
hook.initialPage,
));
properties.add(DiagnosticsProperty<RetryConfig>(
'retryConfig',
hook.retryConfig,
));
properties.add(DiagnosticsProperty<RefreshConfig>(
'refreshConfig',
hook.refreshConfig,
));
properties.add(DiagnosticsProperty<JsonConfig<DataType>>(
'jsonConfig',
hook.jsonConfig,
));
properties
.add(DiagnosticsProperty<ValueChanged<PageEvent<DataType, PageType>>>(
'onData',
hook.onData,
));
properties
.add(DiagnosticsProperty<ValueChanged<PageEvent<ErrorType, PageType>>>(
'onError',
hook.onError,
));
properties.add(DiagnosticsProperty<bool>(
'enabled',
hook.enabled,
));
properties.add(DiagnosticsProperty<String>(
'queryKey',
hook.queryKey,
));
}
return query;
}
+38 -201
View File
@@ -1,8 +1,8 @@
import 'dart:async';
import 'package:fl_query/fl_query.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/diagnostics.dart';
import 'package:fl_query_hooks/src/use_query_client.dart';
import 'package:fl_query_hooks/src/utils/use_updater.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
Mutation<DataType, ErrorType, VariablesType>
@@ -17,227 +17,64 @@ Mutation<DataType, ErrorType, VariablesType>
List<String>? refreshInfiniteQueries,
List<Object?>? keys,
}) {
return use(
UseMutation(
final rebuild = useUpdater();
final client = useQueryClient();
final mutation = useMemoized(() {
return client.createMutation<DataType, ErrorType, VariablesType>(
mutationKey,
mutationFn,
retryConfig: retryConfig,
onData: onData,
onError: onError,
onMutate: onMutate,
refreshQueries: refreshQueries,
refreshInfiniteQueries: refreshInfiniteQueries,
keys: keys,
),
);
}
}, [client, mutationKey]);
class UseMutation<DataType, ErrorType, VariablesType, RecoveryType>
extends Hook<Mutation<DataType, ErrorType, VariablesType>> {
final MutationFn<DataType, VariablesType> mutationFn;
final String mutationKey;
final recoveryData = useState<RecoveryType?>(null);
final RetryConfig retryConfig;
useEffect(() {
return mutation.addListener(rebuild);
}, [mutation]);
final MutationOnDataFn<DataType, RecoveryType>? onData;
final MutationOnErrorFn<ErrorType, RecoveryType>? onError;
final MutationOnMutationFn<VariablesType, RecoveryType>? onMutate;
useEffect(() {
mutation.updateMutationFn(mutationFn);
return null;
}, [mutationFn, mutation]);
// hook specific
final List<String>? refreshQueries;
final List<String>? refreshInfiniteQueries;
useEffect(() {
if (onMutate != null) {
return mutation.mutationStream.listen((event) async {
recoveryData.value = await onMutate.call(event);
}).cancel;
}
return null;
}, [onMutate, mutation]);
const UseMutation(
this.mutationKey,
this.mutationFn, {
this.retryConfig = DefaultConstants.retryConfig,
this.onData,
this.onError,
this.onMutate,
this.refreshQueries,
this.refreshInfiniteQueries,
super.keys,
});
@override
createState() =>
_UseMutationState<DataType, ErrorType, VariablesType, RecoveryType>();
}
class _UseMutationState<DataType, ErrorType, VariablesType, RecoveryType>
extends HookState<Mutation<DataType, ErrorType, VariablesType>,
UseMutation<DataType, ErrorType, VariablesType, RecoveryType>> {
Mutation<DataType, ErrorType, VariablesType>? mutation;
VoidCallback? removeListener;
StreamSubscription<VariablesType>? mutationSubscription;
useEffect(() {
StreamSubscription<DataType>? dataSubscription;
StreamSubscription<ErrorType>? errorSubscription;
RecoveryType? recoveryData;
void rebuild([_]) {
setState(() {});
dataSubscription = mutation.dataStream.listen((event) {
final data = onData?.call(event, recoveryData.value);
if (refreshQueries != null) {
client.refreshQueries(refreshQueries);
}
void subscribeOnMutate() {
if (hook.onMutate != null)
mutationSubscription = mutation!.mutationStream.listen(
(event) async {
recoveryData = await hook.onMutate?.call(event);
if (hook.onData != null) {
dataSubscription?.cancel();
subscribeOnData();
}
if (hook.onError != null) {
errorSubscription?.cancel();
subscribeOnError();
}
},
);
}
void subscribeOnData() {
if (hook.onData != null ||
hook.refreshInfiniteQueries != null ||
hook.refreshQueries != null)
dataSubscription = mutation!.dataStream.listen(
(event) {
final data = hook.onData?.call(event, recoveryData);
if (hook.refreshQueries != null) {
QueryClient.of(context).refreshQueries(hook.refreshQueries!);
}
if (hook.refreshInfiniteQueries != null) {
QueryClient.of(context)
.refreshInfiniteQueries(hook.refreshInfiniteQueries!);
if (refreshInfiniteQueries != null) {
client.refreshInfiniteQueries(refreshInfiniteQueries);
}
return data;
},
);
}
});
void subscribeOnError() {
if (hook.onError != null)
errorSubscription = mutation!.errorStream.listen(
if (onError != null) {
errorSubscription = mutation.errorStream.listen(
(event) {
return hook.onError?.call(event, recoveryData);
return onError(event, recoveryData.value);
},
);
}
Future<void> initialize() async {
setState(() {
_createMutation();
subscribeOnMutate();
subscribeOnData();
subscribeOnError();
removeListener = mutation!.addListener(rebuild);
});
}
void _createMutation() {
mutation = QueryClient.of(context).createMutation(
hook.mutationKey,
hook.mutationFn,
retryConfig: hook.retryConfig,
);
}
@override
void initHook() {
super.initHook();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await initialize();
});
}
@override
void dispose() {
mutationSubscription?.cancel();
return () {
dataSubscription?.cancel();
errorSubscription?.cancel();
removeListener?.call();
super.dispose();
}
};
}, [onData, onError, recoveryData.value, mutation]);
@override
void didUpdateHook(oldHook) {
super.didUpdateHook(oldHook);
if (oldHook.mutationKey != hook.mutationKey) {
mutationSubscription?.cancel();
dataSubscription?.cancel();
errorSubscription?.cancel();
removeListener?.call();
initialize();
return;
}
if (oldHook.mutationFn != hook.mutationFn) {
mutation!.updateMutationFn(hook.mutationFn);
}
if (oldHook.onMutate != hook.onMutate) {
mutationSubscription?.cancel();
subscribeOnMutate();
}
if (oldHook.onData != hook.onData ||
oldHook.refreshQueries != hook.refreshQueries ||
oldHook.refreshInfiniteQueries != hook.refreshInfiniteQueries) {
dataSubscription?.cancel();
subscribeOnData();
mutationSubscription?.cancel();
subscribeOnMutate();
}
if (oldHook.onError != hook.onError) {
errorSubscription?.cancel();
subscribeOnError();
}
}
@override
build(BuildContext context) {
if (mutation == null) {
_createMutation();
}
return mutation!;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(
DiagnosticsProperty<Mutation<DataType, ErrorType, VariablesType>>(
'mutation', mutation),
);
properties.add(
DiagnosticsProperty<String>('mutationKey', hook.mutationKey),
);
properties.add(
DiagnosticsProperty<MutationFn<DataType, VariablesType>>(
'mutationFn', hook.mutationFn),
);
properties.add(
DiagnosticsProperty<RetryConfig>('retryConfig', hook.retryConfig),
);
properties.add(
DiagnosticsProperty<MutationOnDataFn<DataType, RecoveryType>>(
'onData',
hook.onData,
),
);
properties.add(
DiagnosticsProperty<MutationOnErrorFn<ErrorType, RecoveryType>>(
'onError',
hook.onError,
),
);
properties.add(
DiagnosticsProperty<MutationOnMutationFn<VariablesType, RecoveryType>>(
'onMutation',
hook.onMutate,
),
);
}
return mutation;
}
+34 -164
View File
@@ -1,8 +1,9 @@
import 'dart:async';
import 'package:fl_query/fl_query.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:fl_query_hooks/src/use_query_client.dart';
import 'package:fl_query_hooks/src/utils/use_updater.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
Query<DataType, ErrorType> useQuery<DataType, ErrorType>(
@@ -15,185 +16,54 @@ Query<DataType, ErrorType> useQuery<DataType, ErrorType>(
final ValueChanged<DataType>? onData,
final ValueChanged<ErrorType>? onError,
// widget specific
// hook specific
final bool enabled = true,
}) {
return use(
UseQuery<DataType, ErrorType>(
final rebuild = useUpdater();
final client = useQueryClient();
final query = useMemoized(() {
return client.createQuery<DataType, ErrorType>(
queryKey,
queryFn,
initial: initial,
retryConfig: retryConfig,
refreshConfig: refreshConfig,
jsonConfig: jsonConfig,
onData: onData,
onError: onError,
enabled: enabled,
),
refreshConfig: refreshConfig,
retryConfig: retryConfig,
);
}
}, [client, queryKey]);
class UseQuery<DataType, ErrorType> extends Hook<Query<DataType, ErrorType>> {
final QueryFn<DataType> queryFn;
final String queryKey;
useEffect(() {
return query.addListener(rebuild);
}, [query]);
final DataType? initial;
useEffect(() {
query.updateQueryFn(queryFn);
return null;
}, [queryFn, query]);
final RetryConfig retryConfig;
final RefreshConfig refreshConfig;
final JsonConfig<DataType>? jsonConfig;
final ValueChanged<DataType>? onData;
final ValueChanged<ErrorType>? onError;
// hook specific
final bool enabled;
const UseQuery(
this.queryKey,
this.queryFn, {
this.initial,
this.retryConfig = DefaultConstants.retryConfig,
this.refreshConfig = DefaultConstants.refreshConfig,
this.jsonConfig,
this.onData,
this.onError,
this.enabled = true,
super.keys,
}) : assert(
(jsonConfig != null && enabled) || jsonConfig == null,
'jsonConfig is only supported when enabled is true',
);
@override
HookState<Query<DataType, ErrorType>, UseQuery<DataType, ErrorType>>
createState() => _UseQueryState<DataType, ErrorType>();
}
class _UseQueryState<DataType, ErrorType> extends HookState<
Query<DataType, ErrorType>, UseQuery<DataType, ErrorType>> {
Query<DataType, ErrorType>? query;
VoidCallback? removeListener;
useEffect(() {
if (enabled) {
query.fetch();
}
return null;
}, [enabled]);
useEffect(() {
StreamSubscription<DataType>? dataSubscription;
StreamSubscription<ErrorType>? errorSubscription;
void rebuild([_]) {
setState(() {});
if (onData != null) {
dataSubscription = query.dataStream.listen(onData);
}
if (onError != null) {
errorSubscription = query.errorStream.listen(onError);
}
void _createQuery() {
query = QueryClient.of(context).createQuery(
hook.queryKey,
hook.queryFn,
initial: hook.initial,
retryConfig: hook.retryConfig,
refreshConfig: hook.refreshConfig,
jsonConfig: hook.jsonConfig,
);
}
Future<void> initialize() async {
setState(() {
_createQuery();
if (hook.onData != null)
dataSubscription = query!.dataStream.listen(hook.onData);
if (hook.onData != null)
errorSubscription = query!.errorStream.listen(hook.onError);
removeListener = query!.addListener(rebuild);
});
if (hook.enabled) {
await query!.fetch();
}
}
@override
void initHook() {
super.initHook();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await initialize();
});
}
@override
void dispose() {
return () {
dataSubscription?.cancel();
errorSubscription?.cancel();
removeListener?.call();
super.dispose();
}
};
}, [onData, onError, query]);
@override
void didUpdateHook(
UseQuery<DataType, ErrorType> oldHook,
) {
super.didUpdateHook(oldHook);
if (oldHook.queryKey != hook.queryKey) {
dataSubscription?.cancel();
errorSubscription?.cancel();
removeListener?.call();
initialize();
return;
} else if (oldHook.enabled != hook.enabled && hook.enabled) {
query!.fetch();
}
if (oldHook.queryFn != hook.queryFn) {
query!.updateQueryFn(hook.queryFn);
}
if (oldHook.onData != hook.onData) {
dataSubscription?.cancel();
if (hook.onData != null)
dataSubscription = query!.dataStream.listen(hook.onData);
}
if (oldHook.onError != hook.onError) {
errorSubscription?.cancel();
if (hook.onError != null)
errorSubscription = query!.errorStream.listen(hook.onError);
}
}
@override
Query<DataType, ErrorType> build(BuildContext context) {
if (query == null) {
_createQuery();
}
return query!;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(
DiagnosticsProperty<Query<DataType, ErrorType>>('query', query),
);
properties.add(
DiagnosticsProperty<String>('queryKey', hook.queryKey),
);
properties.add(DiagnosticsProperty<DataType>('initial', hook.initial));
properties.add(
DiagnosticsProperty<RetryConfig>('retryConfig', hook.retryConfig),
);
properties.add(
DiagnosticsProperty<RefreshConfig>(
'refreshConfig',
hook.refreshConfig,
),
);
properties.add(
DiagnosticsProperty<JsonConfig<DataType>>(
'jsonConfig',
hook.jsonConfig,
),
);
properties.add(
DiagnosticsProperty<ValueChanged<DataType>>('onData', hook.onData),
);
properties.add(
DiagnosticsProperty<ValueChanged<ErrorType>>('onError', hook.onError),
);
properties.add(DiagnosticsProperty<bool>('enabled', hook.enabled));
}
return query;
}