rrousselGit / flutter_hooks

React hooks for Flutter. Hooks are a new kind of object that manages a Widget life-cycles. They are used to increase code sharing between widgets and as a complete replacement for StatefulWidget.
MIT License
3.13k stars 179 forks source link

Examples for complex cases with useState hook #347

Open Tagir-A opened 1 year ago

Tagir-A commented 1 year ago

Describe what scenario you think is uncovered by the existing examples/articles

Hello! I'm new to Dart and Flutter, but I've used React a lot. Since this library is a port of React hooks, I'd suggest to provide more examples of how hooks could be used. Especially the cases, where it's different from react. I'd suggest to have an example section, which could be filled with the help of the community.

Describe why existing examples/articles do not cover this case

I struggle to understand even such a simple hook as useState in Flutter. It seems that Dart (or Flutter, or this library) behave different from React hooks when complex objects are passed.

For example, I specifically struggle with this situation: I have a Sealed Union built by freezed as a representation of the state. When the exercise prop is passed, I expect the state to initialise. This works for the render of the widget. However, after the exercise prop changes in the parent widget, the state keeps the previous value.

I'm not sure what is the issue. Maybe I misunderstand how Flutter works, but I'd definitely expect the desired behaviour in React.

Code for reference:

// ...
@freezed
class Answer with _$Answer {
  const factory Answer.valid(String value) = Valid;
  const factory Answer.invalid(String value) = Invalid;
}

@freezed
class FormState with _$FormState {
  const factory FormState.initial(MultipleChoiceExercise exercise) = Initial;
  const factory FormState.answered(Answer answer) = Answered;
  const factory FormState.showingCorrect(
      Answer answer, MultipleChoiceExercise exercise) = ShowingCorrect;
}

class MultipleChoiceForm extends HookWidget {
  final MultipleChoiceExercise exercise;

  const MultipleChoiceForm({
    required this.exercise,
// ...
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final formState = useState<FormState>(FormState.initial(exercise));
    // ...

It would be wonderful to have complex cases as examples somewhere in the docs. I'd gladly contribute myself, if you agree with the notion proposed.

rrousselGit commented 1 year ago

When the exercise prop is passed, I expect the state to initialise. This works for the render of the widget. However, after the exercise prop changes in the parent widget, the state keeps the previous value.

What does that mean exactly? I do not understand this bit.

Tagir-A commented 1 year ago

I'll try to explain, using React references. Again, I'm very new to Flutter and Dart, sorry about that.

In react, when I pass an object like so

const [someObject, setSomeObject] = useState({})
return <MultipleChoiceForm exercise={someObject} />

the child re-renders when that object is replaced in the parent. For example:

// Parent code
setSomeObject(newObject)

will trigger a re-render of <MultipleChoiceForm exercise={someObject} /> so inside child it would trigger a new state initialiser.

// MultipleChoiceForm code
const [formState] = useState(FormState.initial(exercise));

I tried to do the same in Flutter. But it's not working. I receive an updated prop inside widget, but it does not trigger a state initialiser for the hook.

I hope now the problem is more clear.

rrousselGit commented 1 year ago

You mean that if useState(false) changes to useState(true), the state resets?

rrousselGit commented 1 year ago

The state never resets for me on React.

Maybe you're confused and your state was reset for reset reasons? Like tree shape change or key change, which causes the component to be unmounted.

Tagir-A commented 1 year ago

You mean that if useState(false) changes to useState(true), the state resets?

No, I mean if you have useState(props.someProp), it will reset, when the prop is updated.

rrousselGit commented 1 year ago

That's what I meant. I tried and didn't see this behavior.

Do you maybe have a react example?

Tagir-A commented 1 year ago

Okay, I feel like I made a mistake there. I'll update once I've done investigating.

Tagir-A commented 1 year ago

Indeed you were right. I was confused about React behaviour. Thanks for your help!

However, what do you think about adding more examples? I really feel like there could be a whole "examples" section to highlight good patterns, explain bad patterns and promote a better alternative, explain integrations with some other popular tools.

I understand that mostly we can look at react examples, but it wouldn't hurt to have specific Flutter examples, right?

rrousselGit commented 1 year ago

I don't have time to work on examples. Feel free to contribute some if you want to.