useEffect는 StatelessWidget
및 StatefulWidget
의 initState
부터 dispose
까지를 사용이 가능하도록 만들어진 함수입니다.
등의 상황에 따라 useEffect를 다양한 방법으로 호출할 수 있습니다.
initState
처럼 사용한 경우.import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class BasicEx extends StatelessWidget {
const BasicEx({super.key});
Widget build(BuildContext context) {
// StatelessWidget 및 StatefultWidget의 initState와 동일하게 실행됩니다.
useEffect(() {
log('initState 효과');
return; // 혹은 return (){};
}, []);
return Container();
}
}
이런 경우 메서드를 호출하는거나 다름이 없습니다.
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class BasicEx extends StatelessWidget {
const BasicEx({super.key});
Widget build(BuildContext context) {
// useEffect가 void useEffect(void Function()? Function() effect, [List<Object?>? keys]) 타입 입니다.
// keys값을 입력하지 않으면 매 순간 useEffect를 호출하게 됩니다.
useEffect(() {
log('메서드 하나 만드시는 것과 동일한 효과를 지닙니다.');
return;
});
return Container();
}
}
Flutter의 flutter_hooks
라이브러리 사용 시, 매개변수로 받아온 값에 대한 변동이 생길 경우에 useEffect
메서드를 실행하도록 코드를 작성하는 와중에,
useState
함수로 매개변수의 값을 초기화한 변수의 값을 Text 위젯에 넣어 표기되도록 하려 했으나, 값의 변화가 반영이 되지 않는 이슈를 발견하게 되었습니다.
이유를 찾고보니 Flutter의 flutter_hooks
라이브러리에서 제공하는 use
로 시작되는 함수들은 대부분 객체가 빌드 사이클 동안 유지된다고 합니다.
이는 React의 hook 모델을 따르며, 위젯이 재빌드 될 때마다 상태나 객체가 초기화 되지 않도록 설계되었습니다.
아래의 데모는 위의 값이 매개변수로 보낼 값 및 증가되는 값, 아래 값이 매개변수로 받는 값 입니다.
매개변수를 그대로 가져오기 때문에 값의 변동대로 표기됩니다.
class BasicCounterEx extends StatelessWidget {
const BasicCounterEx({super.key, required this.outCounter});
final int outCounter;
Widget build(BuildContext context) {
var counter = outCounter;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(counter.toString()),
],
),
);
}
}
use
로 시작되는 메서드들은 대부분 빌드 사이클 동안 객체가 유지 되기 때문에,
재빌드 되더라도 var counter
의 값은 outCounter
의 초기화 값 그대로 유지됩니다.
outCounter의 값은 변경되어 전달되기 때문에 변동이 있을 때마다 useEffect가 실행됩니다.
class SendCounterEx extends HookWidget {
const SendCounterEx({super.key, required this.outCounter});
final int outCounter;
Widget build(BuildContext context) {
// use로 시작되는 메서드들은 대부분 빌드 사이클 동안 객체가 유지됩니다.
// 따라서 outCounter의 값이 변경되어 매개변수값을 전달하여 재빌드 되더라도
// counter의 값은 outCounter의 초기화 값 그대로 표기되어 변경되지 않고 보여집니다.
var counter = useState(outCounter);
useEffect(() {
log('sendCounter useEffect 발동 ${counter.value}');
return;
// outCounter의 변경이 있을 경우 재빌드 합니다.
// useEffect도 재실행 됩니다.
}, [outCounter]);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(counter.value.toString()),
],
),
);
}
}
class SendCounterEx extends HookWidget {
const SendCounterEx({super.key, required this.outCounter});
final int outCounter;
Widget build(BuildContext context) {
useEffect(() {
log('sendCounter useEffect 발동');
return;
// outCounter의 값이 변경되어 들어올때마다 useEffect가 실행됩니다.
// build 메서드가 재빌드 됩니다.
}, [outCounter]);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(outCounter.toString()),
],
),
);
}
}
class UseEffectExPage extends HookWidget {
const UseEffectExPage({super.key});
Widget build(BuildContext context) {
final counter = useState(1);
// useEffect가 void useEffect(void Function()? Function() effect, [List<Object?>? keys])
// 이므로 keys에 배열로 들어간다.
// keys 안의 값은 build 메서드 위젯 안에서의 값은 넣을 수 있지만
// 값의 변동사항이 생기더라도 useEffect가 호출되지 않는다.
// class 멤버 변수인 경우에만 변동사항이 있을 경우 호출되는 것 같다.
useEffect(() {
log('useEffect 발동');
return;
// outCounter의 변경이 있을 경우 재빌드 합니다.
// useEffect도 재실행 됩니다.
}, []);
return Scaffold(
appBar: AppBar(),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(counter.value.toString()),
const SizedBox(height: 30),
ElevatedButton(
onPressed: () {
counter.value++;
},
child: const Text('increase counter'),
),
const SizedBox(height: 50),
SendCounterEx(outCounter: counter.value),
// BasicCounterEx(outCounter: counter.value),
],
),
)),
);
}
}
class SendCounterEx extends HookWidget {
const SendCounterEx({super.key, required this.outCounter});
final int outCounter;
Widget build(BuildContext context) {
// use로 시작되는 메서드들은 대부분 빌드 사이클 동안 객체가 유지됩니다.
// var counter = useState(outCounter);
// useEffect(() {
// log('sendCounter useEffect 발동 ${counter.value}');
// return;
// }, [outCounter]);
useEffect(() {
log('sendCounter useEffect 발동');
return;
}, [outCounter]);
// return Center(
// child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(counter.value.toString()),
// ],
// ),
);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(outCounter.toString()),
],
),
);
}
}