0

I'm studying Flutter hooks. I have written a program that is supposed to exhibit the same behavior as Flutter and React hooks. However, I have noticed that the timing of cleanup execution in useEffect differs from React. I have read the official documentation for flutter_hooks, but I am still unclear about the reason behind this difference.

Is there anyone who can help me understand this better and provide some guidance?

Below is the program I wrote and the result of running it.

Flutter Program

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

void main() {
  runApp(const MyHomePage());
}

class MyHomePage extends HookWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    print("build start");
    final counter = useState<int>(0);

    useEffect(() {
      print("useEffect");
      return () {
        print("useEffect cleanup");
      };
    }, [counter.value]);
    return Directionality(
        textDirection: TextDirection.ltr,
        child: Column(
          children: [
            Center(child: Text('${counter.value}')),
            TextButton(
                onPressed: () {
                  counter.value = counter.value + 1;
                  print('counter incremented');
                },
                child: const Text('+'))
          ],
        ));
  }
} 

React

index.jsx

import React from 'react';
import ReactDOM from 'react-dom/client';

import { App } from './App.jsx'

ReactDOM.createRoot( 
  document.querySelector('#root')
).render(<App />)

App.jsx

import {useState, useEffect} from 'react';

export function App(props) {
  console.log("build start");
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("useEffect");
    return () => {
      console.log("useEffect cleanup");
    };
  }, [count]);

  const button1 = () => {
    setCount((c) => c + 1);
    console.log('counter incremented')
  };

  return (
    <div className='App'>
      <h1>{count}</h1>
      <button onClick={button1}>+</button>
    </div>
  );
}

Result(Flutter program)

build start
useEffect
counter incremented
build start
useEffect
useEffect cleanup
counter incremented
build start
useEffect
useEffect cleanup

Result(React program)

build start
useEffect
counter incremented
build start
useEffect cleanup
useEffect
counter incremented
build start
useEffect cleanup
useEffect

Thanks :)

mmm
  • 3
  • 2

1 Answers1

0

I solved this myself, so I'll leave it here. In conclusion, the order didn't matter. On the surface, handling side effects and cleanup seems to be the opposite of React, but the goal of cleaning up the previous side effects has been achieved.

As a reference, I will leave a note of what I checked the implementation of flutter_hooks. flutter_hooks keeps state in LinkedList format and compares keys each time build is run. And when swapping states, dispose the previous state. By defining lifecycle methods such as initHook, didUpdateHook, and dispose in each Hook, they will be called appropriately along with the above state comparison. (Initial generation, when the keys are the same, when they are different, processing at unmount)

And regarding dispose, which is related to this question, due to the internal implementation, the state targeted for dispose is stored in a list once, and then collectively disposed at the end of the build method. https://github.com/rrousselGit/flutter_hooks/blob/master/packages/flutter_hooks/lib/src/framework.dart#L479 https://github.com/rrousselGit/flutter_hooks/blob/master/packages/flutter_hooks/lib/src/framework.dart#L441

So the cleanup is apparently done after the side effects are processed, but since it's done in the build, it's done before the rendering process anyway. In other words, if you use it for the same purpose as React's useEffect, there should be no unintended behavior.

mmm
  • 3
  • 2