フロントエンドエンジニアのための Flutter Hooks

# はじめに

[Flutter Hooks](https://pub.dev/packages/flutter_hooks) は Flutter で [React Hooks](https://ja.reactjs.org/docs/hooks-intro.html) のように `useState` `useEffect` `useCallback` などのフックを使用できるライブラリです。
この記事では上記で挙げた3つのフックに焦点を当て、 Flutter Hooks と React Hooks の違いをまとめます。

# useState

ローカルなステートを扱うためのフックです。
簡単なカウンターコンポーネント（カウンターウィジェット）を作成して違いを見ていきます。

## React Hooks

参考: [フック API リファレンス - useState](https://ja.reactjs.org/docs/hooks-reference.html#usestate)

```counter.jsx
import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>count: {count}</p>
      <button
        type="button"
        onClick={() => setCount((prev) => prev + 1)}
      >
        add
      </button>
    </div>
  );
};
```

## Flutter Hooks

`state.value` で値にアクセスできます。また、値を更新するための関数は定義せず、 `state.value` の値を直接書き換えます。

参考: [useState<T> function](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useState.html)

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

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

  @override
  Widget build(BuildContext context) {
    final count = useState(0);

    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Text('count: ${count.value}'),
          TextButton(
            onPressed: () {
              count.value += 1;
            },
            child: const Text('add'),
          ),
        ],
      ),
    );
  }
}
```

# useEffect

副作用を実行することを可能にするフックです。
先程のカウンターコンポーネントを使用し、コンソールに値を表示するようにします。

## React Hooks

参考: [フック API リファレンス - useEffect](https://ja.reactjs.org/docs/hooks-reference.html#useeffect)

```counter.jsx
import React, { useState, useEffect } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log(`count: ${count}`);
  }, [count]);

  return (
    ...
  );
};
```

## Flutter Hooks

React Hooks と書きっぷりはほとんど同じですが、違う点もあります。

> didMount & didUpdate are called after the render
> initState & didUpdateWidget are called before the render
>
> Similarly, when unmounting a Component tree, React will call willUnmount on parents first and go down the tree.
> But Flutter does the opposite. It calls dispose on leaves first and goes up the tree.

引用: https://github.com/rrousselGit/flutter_hooks/issues/63#issuecomment-474994331

参考: [useEffect function](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useEffect.html)

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

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

  @override
  Widget build(BuildContext context) {
    final count = useState(0);

    useEffect(() {
      debugPrint('count: ${count.value}');

      return null;
    }, [count.value]);

    return ...
  }
}
```

# useCallback

メモ化されたコールバックを返すフックです。
先程 `useEffect` 内で記述した処理を関数として定義します。

## React Hooks

参考: [フック API リファレンス - useCallback](https://ja.reactjs.org/docs/hooks-reference.html#usecallback)

```counter.jsx
import React, { useState, useEffect, useCallback } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  const outputLog = useCallback(() => {
    console.log(`count: ${count}`);
  }, [count]);

  useEffect(() => {
    outputLog();
  }, [outputLog]);

  return (
    ...
  );
};
```

## Flutter Hooks

`useCallback` に関しても書きっぷりはほとんど変わりません。
参考: [useCallback<T extends Function> function](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useCallback.html)

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

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

  @override
  Widget build(BuildContext context) {
    final count = useState(0);

    final outputLog = useCallback(() {
      debugPrint('count: ${count.value}');
    }, [count.value]);

    useEffect(() {
      outputLog();
      
      return null;
    }, [outputLog]);

    return ...
  }
}
```

# おわりに

今回 Flutter Hooks と React Hooks を比べてみましたが、ほとんど書き方に差はなく、どちらかの知識があればもう片方も書くことができるのが見て取れたと思います。今まではネイティブアプリを作成する際は React に慣れていることもあり、 React Native を用いることが多かったのですが、 Flutter Hooks を知ってからは Flutter でも同様に開発を行うことができるため選択肢の幅が広がったと思います。