useMemoizedIf
Computes and caches a value only while a condition holds. When the condition is false it returns null instead of running the block, and it recomputes whenever the condition or the keys change.
class OrderSummary extends HookWidget {
final Order order;
final bool isExpanded;
const OrderSummary({super.key, required this.order, required this.isExpanded});
Widget build(BuildContext context) {
// breakdown is computed only while expanded; it is null otherwise.
// It is recomputed when isExpanded or order changes.
final breakdown = useMemoizedIf(isExpanded, () => order.computeBreakdown(taxRate: 0.2), [order]);
return Column(
children: [
Text(order.title),
if (breakdown != null) Text('Total: ${breakdown.total}'), // <- null when collapsed
],
);
}
}
Signature
T? useMemoizedIf<T>(bool condition, T Function() block, [HookKeys keys = hookKeysEmpty]);
It is a thin wrapper over useMemoized: the result is useMemoized(() => condition ? block() : null, [condition, ...keys]). condition is prepended to the keys, so toggling it re-runs the memoization. The return type is always nullable, because null is the value when the condition is false.
Use cases
- Deferring an expensive computation until it is actually needed, e.g. building a detail breakdown only while a section is expanded.
- Producing a value to hand to another hook that treats
nullas a no-op, such as a stream built only once a prerequisite is ready:final stream = useMemoizedIf(isReady, () => buildStream(userId), [userId]);useStreamSubscription(stream, (event) async => handle(event)); // <- no subscription until isReady
Caveats
-
The
keysparameter is positional, not named - unlike most hooks. Pass it as the third argument:// DON'TuseMemoizedIf(isOpen, () => compute(id), keys: [id]); // <- no such named parameter// DOuseMemoizedIf(isOpen, () => compute(id), [id]); -
It is not a substitute for
useIf.useMemoizedIfruns a plain computation, so the block must not call hooks. To run hooks conditionally, useuseIf.// DON'TuseMemoizedIf(isOpen, () => useAutoComputedState(() => load(id))); // <- hooks in the block// DOuseIf(isOpen, () => useAutoComputedState(() => load(id))); -
Like
useMemoized, the result is cached - the block runs only whenconditionor a key changes, not on every build. If the block reads a value that is not inkeys, the cached result goes stale.
See also
- useMemoized - the unconditional version this hook is built on
- useIf - when the block needs to call hooks, not just compute a value
- usePreviousIfNull - another small hook for smoothing over transient
nulls