Closed saty9 closed 1 year ago
made a reproduction not involving futures
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class Reproduction extends HookWidget {
const Reproduction({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final squareInput = useState(2);
final badlyMemoizedSquare = useState<num?>(null);
final isEven = useState(false);
useEffect(() {
isEven.value = (badlyMemoizedSquare.value ?? 0) % 2 == 0;
return null;
}, [badlyMemoizedSquare.value]);
useEffect(() {
debugPrint("effect triggered base: ${squareInput.value}");
badlyMemoizedSquare.value = pow(squareInput.value, 2);
return null;
}, [squareInput.value]);
return Column(children: [Text("square: ${badlyMemoizedSquare.value} is ${isEven.value ? "even" : "odd"}"), TextButton(onPressed: () {squareInput.value += 1;}, child: Text("increase base"))]);
}
}
also found a workaround for now:
Future.delayed(Duration.zero, () async {
useStateResult.value = newFuture;
});
scheduling the change to run like this means its executed after the current render which means the useState correctly causes a re-render
I assume it's because you'll need to mutate the useState value inside a WidgetsBinding.instance.addPostFrameCallback((timeStamp) { });
made a reproduction not involving futures
What's supposed to happen? Why would your widget rebuild?
The newer reproduction should count through square numbers and say if they are even or not instead it counts through square numbers and says if the last one was even or not. If this bug was in react you just wouldn't see the numbers change on the first button press as calling set state doesn't mutate the current state value but a value inside the hook and triggers a rerender. But this implementation you can mutate the result of the hook during a render which is why it doesn't show up in most cases
I think the difference is in react useEffect schedules the function to be run after the frame whereas this implementation does it during the current frame.
if the objective is a flutter implementation of react hooks should effects be changed to run after the frame like they would in react? I know that would probably be a breaking change
useEffect in flutter_hooks is synchronous, to mimic how most Flutter life-cycles are synchronous too.
I have no plan to change this for now. You're free to wrap the state change in a Future(() => state.value = ...
to work around this
is it anti-pattern to update states inside useEffect
?
Describe the bug useState seems to not trigger a rerender if given a new value in a useEffect
To Reproduce
Expected behavior In the example I'd expect pressing the button to cause the text to change to 25 after a second
Actual behaviour It stays on 4
logs: