Flutter/pakages

Flutter Hooks ๋„ˆ๋ž€ ๋†ˆ์€ ๋ญ๋ƒ?! flutter_hooks ํŒŒํ—ค์ณ๋ณด๊ธฐ - useState/useEffect ์‚ฌ์šฉ๋ฒ• (1)

Hac. Dog ๐ŸŒญ 2024. 2. 9. 18:40

 

์ž‘๋…„๋ถ€ํ„ฐ ์ง„ํ–‰ํ•˜๋˜ ๋ชจ์ž„์—์„œ ๋งค์ฃผ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ํŒจํ‚ค์ง€์— ๋Œ€ํ•ด์„œ ๊ฐ์ž ๊ณต๋ถ€ํ•ด์„œ ๋ฐฐ์šด์ ์„ ๊ณต์œ ํ•˜๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์ ธ๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค.

์ด๋ฒˆ์—๋Š” flutter_hooks์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž๊ณ  ์ง„ํ–‰์ด ๋˜์—ˆ๊ณ  ๋‚ด๊ฐ€ ๊ณต๋ถ€ํ•œ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ด๋ณด์ž๊ณ  ํ•œ๋‹ค.

 

hooks์— ๋Œ€ํ•œ ๋‚ด์šฉ์— ๋Œ€ํ•œ ์ฐธ๊ณ ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

pub.dev ๊ณต์‹
https://pub.dev/packages/flutter_hooks
 

flutter_hooks | Flutter package

A flutter implementation of React hooks. It adds a new kind of widget with enhanced code reuse.

pub.dev

๊ฐœ๋ฐœํ•˜๋Š”๋‚จ์ž๋‹˜ ์œ ํŠœ๋ธŒ
https://www.youtube.com/watch?v=GBVBLKESogU&list=PLgRxBCVPaZ_32XE17iRqS0y6cq3tQii48

 


๊ทธ๋ ‡๋‹ค๋ฉด hooks์ด๋ž€ ๋ฌด์—‡์ผ๊นŒ?

์ผ๋‹จ hook์ด๋ž€ ๊ฐˆ๊ณ ๋ฆฌ๋ž€ ๋œป์ด๋‹ค. ์ฐพ์•„๋ณด๊ธฐ ์ด ๊ฐœ๋…์€ React์—์„œ๋ถ€ํ„ฐ ์ฒ˜์Œ ๋‚˜์˜จ ๊ฒƒ ๊ฐ™๋‹ค.

์ผ๋‹จ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์–ธ์–ด์—์„œ ํ›…์ด๋ž€ '์›๋ž˜ ์กด์žฌํ•˜๋Š” ์–ด๋–ค ๊ธฐ๋Šฅ์— ๊ฐˆ๊ณ ๋ฆฌ๋ฅผ ๊ฑฐ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ผ์–ด๋“ค์–ด๊ฐ€ ๊ฐ™์ด ์ˆ˜ํ–‰' ํ•˜๋Š” ์˜๋ฏธ๋ผ๊ณ  ํ•œ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ๊ฒฐ๊ตญ ํ›… ํŒจํ‚ค์ง€๋Š” dart์ฝ”๋“œ๋ฅผ ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ฐ„๊ฒฐํ•˜๊ณ  ์‰ฝ๊ฒŒ ๋งŒ๋“ค๊ฒŒ ๋„์™€์ฃผ๋Š” ๋ณด์กฐ์—ญํ™œ ์ˆ˜๋‹จ์ด๋ผ ์ƒ๊ฐํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

 

์‹ค์ œ ํ›… ๊ฐœ๋ฐœ์ž๋“ค์˜ ๊ณต์‹๋ฌธ์„œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด

'์ค‘๋ณต์„ ์ œ๊ฑฐํ•˜๊ณ  ์œ„์ ฏ๊ฐ„์˜ ์ฝ”๋“œ ์ƒ์‚ฐ์„ฑ์„ ์ฆ๊ฐ€์‹œํ‚ค๊ธฐ ์œ„ํ•จ์ด๋‹ค.' ๋ผ๊ณ  ์ ํ˜€์žˆ๋‹ค.

์ฆ‰ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ(Boiler Plate code)๋ฅผ ์ตœ๋Œ€ํ•œ ์ค„์ด๊ธฐ ์œ„ํ•ด ์ด ํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค๊ณ  ์ดํ•ดํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.

 

์‹ค์ œ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด์„œ ํ›…์˜ ์œ ์šฉํ•จ์„ ๋ณด์ž!

class Example extends StatefulWidget {
  final Duration duration;

  const Example({Key? key, required this.duration})
      : super(key: key);

  @override
  _ExampleState createState() => _ExampleState();
}

class _ExampleState extends State<Example> with SingleTickerProviderStateMixin {
  AnimationController? _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: widget.duration);
  }

  @override
  void didUpdateWidget(Example oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.duration != oldWidget.duration) {
      _controller!.duration = widget.duration;
    }
  }

  @override
  void dispose() {
    _controller!.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

์œ„ ์ฝ”๋“œ๋Š” ํ”Œ๋Ÿฌํ„ฐ์—์„œ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ์ด๋‹ค.

์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” 

initState(){} // ์ดˆ๊ธฐ ์„ค์ •
didUpdateWidget(){} // ์—…๋ฐ์ดํŠธ ์„ค์ •
dispose(){} // ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ œ๊ฑฐ

์ด๋ ‡๊ฒŒ 3๊ฐœ์˜ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์ƒ๋ช…์ฃผ๊ธฐ(LifeCycle)์„ ์ƒ๊ฐํ•˜๋ฉด์„œ ๊ตฌํ˜„ํ•ด์•ผ ๋œ๋‹ค.

๊ฐœ๋ฐœ์„ ํ•˜๋ฉด์„œ ์‚ฌ์‹ค ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š๊ณ (์˜ˆ๋ฅผ๋“ค์–ด ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ• ๋‹นํ•˜๊ณ  dispose()๋ฅผ ํ•˜์ง€ ์•Š๊ณ  ๋‹ค๋ฅธ ํ™”๋ฉด์œผ๋กœ ์ „ํ™˜ํ•œ๋‹ค๋˜๊ฐ€ ๋“ฑ)

์ด๋ ‡๊ฒŒ ๊ฐœ๋ฐœ์„ ํ–ˆ๋Š”๋ฐ ์ž‘์€ ๊ทœ๋ชจ์˜ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด ๊ดœ์ฐฎ์„์ง€ ๋ชฐ๋ผ์„œ ํฐ ๊ทœ๋ชจ๋ผ๋ฉด ๋ถ„๋ช…ํžˆ ๋ฉ”๋ชจ๋ฆฌ์— ์“ธ๋ชจ์—†๋Š” ๋‚ญ๋น„๊ฐ€ ์žˆ์„ ๊ฒƒ์ด๊ณ  ์ข‹์€ ์ฝ”๋“œ๋ผ ๋ณผ ์ˆ˜๋Š” ์—†์„ ๊ฒƒ์ด๋‹ค.

 

class Example extends HookWidget {
  const Example({Key? key, required this.duration})
      : super(key: key);

  final Duration duration;

  @override
  Widget build(BuildContext context) {
    final controller = useAnimationController(duration: duration);
    return Container();
  }
}

ํ•˜์ง€๋งŒ ์œ„์™€๊ฐ™์ด ํ›…์„ ์“ด๋‹ค๋ฉด ์งง์€ ์ฝ”๋“œ๋กœ๋„ ๋˜‘๊ฐ™์€ ์„ฑ๋Šฅ์„ ๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

์‹ค์ œ ์œ„ ๋‘ ์ฝ”๋“œ๋Š” 100% ๋˜‘๊ฐ™์€ ๋™์ž‘๋ฐฉ์‹์œผ๋กœ ์‹คํ–‰๋œ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ๋‹ค๋ฅธ ๋กœ์ง๋“ค์€ ์–ด๋””์— ์žˆ๋Š”๊ฒƒ์ธ๊ฐ€? ๋ผ๊ณ  ์ƒ๊ฐ์ด ๋“ค ์ˆ˜ ์žˆ๋‹ค.

 

์ด๋Š” ํ›…์— ๋‹ค ๊ตฌํ˜„์ด ๋˜์–ด์žˆ๊ธฐ์— ์šฐ๋ฆฌ๋Š” ๊ตฌํ˜„๋œ ๊ธฐ๋Šฅ๋“ค๋งŒ ๊ฐ€์ ธ๋‹ค๊ฐ€ ์“ฐ๋ฉด ๋œ๋‹ค.

 

์ด์ œ ์–ด๋–ค ๊ธฐ๋Šฅ๋“ค์ด ์žˆ๋Š”์ง€ ๊ฐ„๋‹จํ•˜๊ฒŒ ์•Œ์•„๋ณด์ž.

 

No. Name Description
1 useState ๋ณ€์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค.
2 useEffect ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ฑฐ๋‚˜ ์„ ํƒ์ ์œผ๋กœ ์ทจ์†Œํ•˜๊ธฐ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
3 useMemoized ๋‹ค์–‘ํ•œ ๊ฐ์ฒด์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์บ์‹ฑํ•ฉ๋‹ˆ๋‹ค.
4 useStream Stream์„ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค. AsyncSnapShot์œผ๋กœ ํ˜„์žฌ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
5 useFuture Future๋ฅผ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค. AsyncSnapShot์œผ๋กœ ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
6 useAnimation Animation์„ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ฐ์ฒด์˜ value๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
7 useReducer state๊ฐ€ ์กฐ๊ธˆ ๋” ๋ณต์žกํ• ๋•Œ, useState ๋Œ€์‹  ์‚ฌ์šฉํ•  ๋Œ€์•ˆ์ž…๋‹ˆ๋‹ค.
8 useTextEditingController TextEditingController๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
9 useTabController TabController๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
10 useScrollController ScrollController๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
11 usePageController ScrollController๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

 


1. useState

์œ„ ์ฝ”๋“œ๋Š” ๋ณ€์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ตฌ๋…์„ ํ•œ๋‹ค๊ณ  ์ ํ˜€์žˆ๋‹ค.

์ด๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ Getx์— .obs๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ํŽธํ•  ๊ฒƒ ๊ฐ™๋‹ค.

์ฆ‰ ๋‚ด๊ฐ€ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๊ณ  ๊ทธ ๋ณ€์ˆ˜์˜ ์ƒํƒœ๊ฐ€ ๋ณ€ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ์ž๋™์œผ๋กœ ์žฌ๋นŒ๋“œ๋ฅผ ํ•ด์ค€๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

์ฆ‰, ์ž๋™์œผ๋กœ setState(){}๋ฅผ ํ•ด์ฃผ๋Š” ์—ญํ™œ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

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

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

    return Scaffold(
      appBar: AppBar(
        title: const Text('useState'),
      ),
      body: Center(
        child: Text(
          counter.value.toString(),
          style: const TextStyle(
            fontSize: 32,
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => counter.value++,
        child: const Icon(Icons.add),
      ),
    );
  }
}

 

์œ„ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด counter๋ณ€์ˆ˜์— useState(0)๋ฅผ ํ• ๋‹นํ•ด ์ฃผ๊ณ  ์žˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๋œ๋‹ค๋ฉด 0์ด๋ผ๋Š” intํ˜•์„ ์ž๋™์œผ๋กœ ํƒ€์ž…์„ ์ง€์ •ํ•ด์„œ ์ด์ œ๋ถ€ํ„ฐ ๋ณ€์ˆ˜์˜ ์ƒํƒœ๋ฅผ ๊ตฌ๋…ํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์žˆ์„๋•Œ๋งˆ๋‹ค

Rebuild๋ฅผ ํ•˜์—ฌ ํ™”๋ฉด์— ์ƒํƒœ๋ฅผ ์ƒˆ๋กญ๊ฒŒ ๋ฐ˜์˜ํ•˜๊ฒŒ ๋œ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ๋‚˜๋Š” ๊ถ๊ธˆ์ฆ์ด ํ•˜๋‚˜ ์ƒ๊ฒผ๋‹ค. Provider์ฒ˜๋Ÿผ ํ™”๋ฉด์„ ์ „๋ถ€ ๊ฐฑ์‹ ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋ณ€์ˆ˜ ๋ถ€๋ถ„๋งŒ ๊ฐฑ์‹ ํ•˜๋Š” ๊ฒƒ์ธ๊ฐ€?

๊ทธ๋ž˜์„œ log๋ฅผ ์ฐ์–ด๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค.

๊ฒฐ๋ก ์€ ํ™”๋ฉด์„ ์ „๋ถ€ ์žฌ๋นŒ๋“œํ•˜๊ฒŒ ๋œ๋‹ค.

 

์•„๋‹ˆ ๊ทธ๋ ‡๋‹ค๋ฉด ๊ทธ๋ƒฅ Stateful ์œ„์ ฏ์— setState๋ฅผ ์“ฐ์ง€ ๊ท€์ฐฎ๊ฒŒ ํŒจํ‚ค์ง€ ์„ค์น˜ํ•˜๊ณ  ์ด๊ฑฐ ๊ณต๋ถ€ํ•ด์•ผ ํ•จ?

๋ผ๊ณ  ์ƒ๊ฐ์ด ๋“ค ์ˆ˜ ์žˆ๋‹ค. ๋งž๋‹ค ํ›…์€ ๊ทธ๋ƒฅ ๋„์›€์„ ์ฃผ๋Š” ํŒจํ‚ค์ง€๋ผ ๊ฒฐ๊ตญ ๊ฐœ์ธ์ด ์„ ํƒํ•˜๊ธฐ ๋‚˜๋ฆ„์ด๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ํ›…์—†์ด ์‚ฌ์šฉํ•ด๋ณธ Stateful๋กœ๋งŒ ์œ„ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์ž‘์„ฑํ•ด๋ณด์ž.

์ขŒ flutter_hooks / ์šฐ StatefulWidget

๋‘ ์ฝ”๋“œ์˜ ์–‘์ฐจ์ด๋กœ ๋ดค์„๋•Œ๋Š” hooks ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•œ๊ฒŒ ๋” ์งง๊ณ  ๊ฐ€๋…์„ฑ ์ธก๋ฉด์—์„œ๋„ ์ข‹์€ ๊ฒƒ ๊ฐ™๋‹ค.

๊ทผ๋ฐ ๊ฒฐ๊ตญ ๊ฐœ๋ฐœ์ž ๋ณธ์ธ์ด ํŽธํ•œ์ชฝ์œผ๋กœ ์„ ํƒํ•˜๋Š”๊ฒŒ ์ข‹๋‹ค. ์ด๊ฒŒ ๋” ์ข‹๋‹ค๋ผ๊ณ  ๋”ฑ ์ž˜๋ผ ๋งํ• ์ˆ˜๋Š” ์—†๋‹ค.

์ž˜์ƒ๊ฐํ•ด๋ณด๊ณ  ์—…๋ฌดํšจ์œจ์ด ์ฆ๊ฐ€ํ•œ๋‹ค๊ณ  ์ƒ๊ฐ๋“œ๋Š” ์ชฝ์œผ๋กœ ์„ ํƒํ•˜๋Š”๊ฒŒ ์˜ณ์€ ์„ ํƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

 

์ „์ฒด ์ฝ”๋“œ
์ฝ”๋“œ ๋ณด๊ธฐ
import 'dart:developer';

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

void main() {
  runApp(
    const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomeScreen(),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    final counter = useState(0);
    log('Rebuild');

    return Scaffold(
      appBar: AppBar(
        title: const Text('useState'),
      ),
      body: Center(
        child: Text(
          counter.value.toString(),
          style: const TextStyle(
            fontSize: 32,
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => counter.value++,
        child: const Icon(Icons.add),
      ),
    );
  }
}


// class HomeScreen extends StatefulWidget {
//   const HomeScreen({super.key});

//   @override
//   State<HomeScreen> createState() => _HomeScreenState();
// }

// class _HomeScreenState extends State<HomeScreen> {
//   int counter = 0;

//   @override
//   Widget build(BuildContext context) {
//     log('Rebuild');

//     return Scaffold(
//       appBar: AppBar(
//         title: const Text('useState'),
//       ),
//       body: Center(
//         child: Text(
//           counter.toString(),
//           style: const TextStyle(
//             fontSize: 32,
//             fontWeight: FontWeight.bold,
//           ),
//         ),
//       ),
//       floatingActionButton: FloatingActionButton(
//         onPressed: () => setState(() {
//           counter++;
//         }),
//         child: const Icon(Icons.add),
//       ),
//     );
//   }
// }

2. useEffect

๋‹ค์Œ์œผ๋กœ๋Š” useEffect์˜ ์‚ฌ์šฉ๋ฒ•์ด๋‹ค.

useState์™€ useEffect ๋‘๊ฐœ์˜ ์ฐจ์ด์ ์€

useEffect๋Š” ์„ ์–ธ๋œ ๋ฆฌ์ŠคํŠธ๋ณ€์ˆ˜์˜ ์ƒํƒœ๊ฐ€ ๋ณ€ํ• ๋•Œ๋งˆ๋‹ค ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋œ๋‹ค๋Š” ์ ์—์„œ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.

๊ธ€๋กœ๋งŒ ๋ณด๋ฉด ์ „ํ˜€ ์ดํ•ด๊ฐ€ ์•ˆ๋˜๋Š”๊ฒŒ ๋‹น์—ฐํ•˜๋‹ˆ ์ฝ”๋“œ์™€ ์˜ˆ์ œ๋ฅผ ๋ณด๋ฉด์„œ ์‰ฝ๊ฒŒ ์ดํ•ดํ•ด ๋ณด์ž!

 

์ผ๋‹จ useEffect๊ฐ€ ๋ฐ›์•„์•ผํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ™•์ธํ•ด๋ณด์ž!

void useEffect(Dispose? Function() effect, [List<Object?>? keys]) {
  use(_EffectHook(effect, keys));
}

(Function(), List<Object>) ์ด๋ ‡๊ฒŒ 2๊ฐœ์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

์ฆ‰, ์ฒ˜์Œ์—๋Š” ํ•จ์ˆ˜์˜ ๊ฐ’์ด ํ•„์š”ํ•˜๊ณ  ๊ทธ ๋‹ค์Œ์œผ๋กœ ๋ฆฌ์ŠคํŠธ๊ฐ€ ์ฃผ์–ด์ ธ์•ผ ํ•œ๋‹ค.

class HomeScreen extends HookWidget {
  HomeScreen({super.key});
  int increment = 0;

  @override
  Widget build(BuildContext context) {
    final counter = useState(0);
    
    // useEffect ์ฝ”๋“œ
    useEffect(() {
      log('useEffect ์‹คํ–‰');
      return () {};
    }, [increment]);
    
    ...
    
    Scaffold(..๊ทธ์™ธ ์ฝ”๋“œ);
 }

๊ฐ„๋‹จํ•˜๊ฒŒ ์ด๋ ‡๊ฒŒ ์ƒ๊ฐํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

์œ„ ์ฝ”๋“œ๋Š” useEffect๊ฐ€ ์‹คํ–‰์ด ๋œ๋‹ค๋ฉด log๋กœ 'useEffect ์‹คํ–‰'์ด ํ”„๋ฆฐํŠธ ๋˜๊ณ ,

return๊ฐ’์œผ๋กœ๋Š” ์•„๋ฌด๊ฒƒ๋„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ฆฌ๊ณ  2๋ฒˆ์งธ ์ธ์ž๋กค increment๋ผ๋Š” intํ˜•์„ ๋ฆฌ์ŠคํŠธ ํ˜•ํƒœ๋กœ ์ฃผ์—ˆ๋‹ค.

์ž ๊ทธ๋ ‡๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ useEffect()๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”์ง€ ์ „์ฒด ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด์„œ ์ดํ•ดํ•ด๋ณด์ž.

 

class HomeScreen extends HookWidget {
  HomeScreen({super.key});
  int increment = 0;

  @override
  Widget build(BuildContext context) {
    final counter = useState(0);
    // useEffect ๊ธฐ๋Šฅ
    useEffect(() {
      log('useEffect ์‹คํ–‰');
      return () {};
    }, [increment]);

    log('Rebuild');
    return Scaffold(
      appBar: AppBar(
        title: const Text('useState'),
      ),
      body: Center(
        child: Text(
          counter.value.toString(),
          style: const TextStyle(
            fontSize: 32,
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          counter.value++;
          // counter๊ฐ’์ด 5์˜ ๋ฐฐ์ˆ˜์ผ๋•Œ increment 1์ฆ๊ฐ€
          if (counter.value % 5 == 0) {
            increment++;
          }
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

 

์œ„ ์ฝ”๋“œ๋ฅผ ์ž์„ธํžˆ ๋ณด๋ฉด counter๊ฐ’์ด 5์˜ ๋ฐฐ์ˆ˜์ผ๋•Œ increment๊ฐ’์„ 1์”ฉ ์ฆ๊ฐ€ ์‹œํ‚จ๋‹ค.

์ด๋•Œ increment์˜ ๊ฐ’์— ๋ณ€ํ™”๊ฐ€ ๊ฐ์ง€๋˜๋ฉด useEffect()๊ฐ€ ์‹คํ–‰์ด ๋˜๊ณ  ๊ทธ ์•ˆ์—์žˆ๋Š” ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰์ด ๋œ๋‹ค.

 

์œ„์™€ ๊ฐ™์ด ์ฒ˜์Œ์— useEffect()๊ฐ€ ํ•œ๋ฒˆ์ด ์‹คํ–‰๋˜๊ณ 

increment์˜ ๊ฐ’์˜ ์ƒํƒœ๊ฐ€ ๋ณ€ํ™”ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด useEffect๊ฐ€ ์‹คํ–‰๋˜๋Š” ์ผ์€ ์—†๋‹ค.

์ž ๊ทธ๋ ‡๋‹ค๋ฉด ์˜๋ฌธ์ด ๋“ค ์ˆ˜ ์žˆ๋‹ค.

 

์•„๋‹ˆ ๊ทธ๋ž˜์„œ useState๋ž‘ useEffect๋ž‘ ๋„๋Œ€์ฒด ๋ญ๊ฐ€ ๋‹ค๋ฅธ๊ฑด๋ฐ!?

 

๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ•˜์ž๋ฉด

useState๋Š” ์ƒํƒœ๊ฐ€ ๋ณ€ํ™”ํ• ๋•Œ๋งˆ๋‹ค ์ „์ฒดํ™”๋ฉด์„ ์ „๋ถ€ ์žฌ๋นŒ๋“œํ•˜๊ฒŒ ๋œ๋‹ค.

ํ•˜์ง€๋งŒ useEffect๋Š” List[]์•ˆ์— ์žˆ๋Š” ๋ณ€์ˆ˜๋“ค์˜ ํ‚ค๊ฐ’์ด ๋ณ€ํ• ๋•Œ๋งˆ๋‹ค ์ƒํƒœ๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

์‚ฌ์šฉ๋ฒ•์ด ๋งž๋Š” ๋ฐฉ์‹์ธ์ง€๋Š” ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ๋„ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

 

๋‚ด๊ฐ€ ์ดํ•ดํ•œ ๋‚ด์šฉ์„ ๊ทธ๋Œ€๋กœ ์˜ฎ๊ฒจ ์ ์—ˆ๋Š”๋ฐ ์กฐ๊ธˆ์ด๋‚˜๋งˆ ์ดํ•ด๊ฐ€ ๋˜์—ˆ์œผ๋ฉด ์ข‹๊ฒ ๋‹ค.

ํ˜น์‹œ ์ถ”๊ฐ€๋กœ ๊ถ๊ธˆํ•œ ์ ์ด ์žˆ๋‹ค๋ฉด ๋Œ“๊ธ€๋กœ ๋‚จ๊ฒจ์ฃผ๊ธฐ ๋ฐ”๋ž€๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋ฐ‘์— ํ’€์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๋ฉด์„œ ์ดํ•ดํ•˜๋ฉด ๋”์šฑ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.

 

https://dartpad.dev/?id=7face35a3c5eb5f24c66a92646754199

 

DartPad

 

dartpad.dev

 

์ „์ฒด ์ฝ”๋“œ
์ฝ”๋“œ ๋ณด๊ธฐ
import 'dart:developer';

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

void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomeScreen(),
    ),
  );
}

// ignore: must_be_immutable
class HomeScreen extends HookWidget {
  HomeScreen({super.key});
  int increment = 0;
  int increment1 = 0;

  @override
  Widget build(BuildContext context) {
    final counter = useState(0);
    // useEffect ๊ธฐ๋Šฅ
    useEffect(() {
      log('useEffect ์‹คํ–‰');
      return () {
        increment1++;
      };
    }, [increment]);

    log('Rebuild');
    return Scaffold(
      appBar: AppBar(
        title: const Text('useState'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              counter.value.toString(),
              style: const TextStyle(
                fontSize: 32,
                fontWeight: FontWeight.bold,
              ),
            ),
            Text(
              increment1.toString(),
              style: const TextStyle(
                fontSize: 32,
                fontWeight: FontWeight.bold,
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          counter.value++;
          // counter๊ฐ’์ด 5์˜ ๋ฐฐ์ˆ˜์ผ๋•Œ increment 1์ฆ๊ฐ€
          if (counter.value % 5 == 0) {
            increment++;
          }
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}