useValueWrapper
Wraps a value in a stable holder whose .value is updated to the latest value on every build. The holder identity stays the same across builds, so a closure can capture it once and still read fresh values.
class LiveLogger extends HookWidget {
final int count;
const LiveLogger({super.key, required this.count});
Widget build(BuildContext context) {
// wrapper.value always reflects the latest count, without re-running the effect.
final wrapper = useValueWrapper(count);
useEffect(() {
final timer = Timer.periodic(
const Duration(seconds: 1),
(_) => debugPrint('count is now ${wrapper.value}'), // <- reads the current value
);
return timer.cancel;
}); // <- No keys: the effect runs once, but still sees fresh values
return Text('$count');
}
}
Signature
Value<T> useValueWrapper<T>(T value);
Returns a Value<T> - read the current value with .value. The returned object is created once (via useMemoized) and its .value is refreshed on each build, so callbacks that captured the wrapper keep seeing up-to-date data.
Use cases
- Reading a frequently-changing value inside a long-lived callback (a timer, a stream listener, an effect with no keys) without recreating the callback every time the value changes
- Avoiding the alternative of adding the value to an effect's keys, when you do not want the effect to re-run on every change
Caveats
-
This is a deliberate escape hatch from the usual keys-based reactivity. Because the wrapper is captured once, changing the wrapped value does not re-run the effect or trigger a rebuild - the callback reads the newer value next time it runs. If you want to react to the change, add the value to the keys instead.
final count = useState(0);// CAREFUL - the effect won't re-run when count changes; it just reads the latest valuefinal wrapper = useValueWrapper(count.value);useEffect(() {final timer = Timer.periodic(const Duration(seconds: 1), (_) => print(wrapper.value));return timer.cancel;}); -
The returned
Value<T>is read-only by design - you cannot assign through it to change the underlying state. To own a mutable value, useuseState.
See also
- useEffect - the captured-values caveat that motivates this hook
- useState - for a value you own and mutate, not just observe
- useMemoized - the caching primitive
useValueWrapperis built on