일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- IOS
- SwiftUI
- 청년취업사관학교후기
- stanford
- MVVM
- ImageSlider
- 스위프트
- UIKit
- WidgetTree
- Masil
- xml
- collectionView
- flutter
- 프로그래머스
- 코딩테스트
- UserDefault
- colorofdays
- 백준
- CS193p
- 오늘의 색상
- 새싹후기
- GIT
- flutter #state # stateful #stateless
- 프로젝트회고
- xcode
- 스터디
- process
- Swift
- 알고리즘
- 조건문
- Today
- Total
개발을 시작하는 이야기
Flutter의 Hooks 사용하기 01 본문
Flutter_hooks는 React에 있던 것을 Flutter에서 구현한 라이브러리다. 이름에서 알수 있듯 갈고리로 원래 있던 객체에 갈고리를 걸어 같이 수행하는 역할을 한다. 추상화를 통해 중복을 제거하고 위젯간의 코드 생산성을 증가시켜주는데에 큰 역할을 한다.
StatelessWidget과 StatefulWidget을 대신하여 HookWidget을 제공해준다. HookWidget을 사용하면 해당 위젯이 가진 Hook을 사용할 수 있다.
StatefulWidget의 큰 단점중 하나는 initState나 dispose에서 로직을 재사용하기가 힘들다는 것이다. 예로 AnimationController를 들 수 있다.
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();
}
}
AnimationController를 구현 한다면 initState(), didUpdateWidget(), dispose(), 3개의 구성요소를 생명주기에 맞춰 구현해야 한다. 이러한 방식은 코더에게 많은 작업을 요구하며, 작은 규모의 프로젝트라면 괜찮을지 몰라도 큰 규모에서는 메모리의 누수를 피할 수 없을 것이다.
Dart의 Mixin은 이 문제를 부분적으로 해결할 수 있지만 모든 문제를 해결하지 못한다. 주어진 Mixin은 클래스당 한 번만 사용할 수 있으며, Mixin과 클래스는 동일한 객체를 공유한다. 이는 동일한 이름으로 변수를 정의하는 경우 컴파일 실패부터 알 수 없는 동작까지 결과가 달라질 수 있음을 의미한다.
Hooks는 이러한 문제들을 보완하는 솔루션을 제공한다.
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();
}
}
이 코드는 이전 예제 코드와 완전히 동일한 동작 방식으로 실행된다. 여전히 AnimationController는 duration마다 업데이트 되고, disposes된다. 하지만 우리에게는 그러한 로직이 보이지 않는다. 이는 Hooks에 이미 다 구현이 되어 있기 때문에 우리는 구현된 기능을 가져다 갈고리처럼 걸어 사용하면 된다.
Hooks가 제공하는 대표적인 기능들은 다음과 같다.
- useState : 변수를 생성하고 구독한다.
- useEffect : 상태를 업데이트 하거나 선택적으로 취소하기에 유용하다.
- useMemorized : 다양한 객체의 인스턴스를 캐싱한다.
- useStream : Stream을 구독하고, AsyncSnapShot으로 현재 상태를 반환한다.
- useFuture : Future를 구독하고, AsyncSnapShot으로 현재 상태를 반환한다.
- useAnimation : Animation을 구독하고 해당 객체의 Value를 반환한다.
- useReducer : state가 복잡해 useState를 사용할 수 없을때 사용한다.
- userTextEditingController : TextEditingController를 생성한다.
- useTapController : TapController를 생성한다.
- useScrollController : ScrollController를 생성한다.
- usePageController : PageController를 생성한다.
이보다 더 많고 다양한 기능을 제공하는데 자세한 내용은 공식문서에 살펴보면 설명이 되어있다.
Hooks를 사용하기 위한 규칙은 다음과 같다.
- 항상 앞에 use를 붙여야 한다.
- 무조건 호출 Hook를 수행해야 한다.
- Hook를 조건으로 묶으면 안된다.
Widget build(BuildContext context) {
// use를 앞에 붙여서 사용해야 하며, 호출 후크를 수행해야 한다.
useMyHook();
// use없이 사용하면 안된다. ❌
myHook();
// ....
// 조건으로 묶으면 안된다. ❌
if (condition) {
useMyHook();
}
// ....
}
Hook를 만드는 방법은 두가지가 있다.
- Function
함수는 가장 일반적인 방법으로, 다른 후크를 결합하여 보다 복잡한 사용자 정의 후크를 생성할 수 있다. 이러한 경우에도 함수 앞에 관례적으로 use를 붙여서 사용한다.
다음 코드는 변수를 생성하고 값이 변경될 때마다 해당 값을 콘솔에 기록하는 사용자 지정 후크이다.
ValueNotifier<T> useLoggedState<T>([T initialData]) {
final result = useState<T>(initialData);
useValueChanged(result.value, (_, __) {
print(result.value);
});
return result;
}
- Class
후크가 너무 복잡해 진다면, 클래스로 확장 변환할 수 있다. 클래스로서 후크는 State클래스와 유사하며, 위젯의 수명주기와 initHook, dispose, setState 같은 메서드에서 접근할 수 있다.
일반적으로 함수 아래 클래스를 숨기는것이 좋으며, 다음 코드는 후크가 state된 시간동안 콘솔에 기록하는 사용자 지정 후크이다.
Result useMyHook() {
return use(const _TimeAlive());
}
class _TimeAlive extends Hook<void> {
const _TimeAlive();
@override
_TimeAliveState createState() => _TimeAliveState();
}
class _TimeAliveState extends HookState<void, _TimeAlive> {
DateTime start;
@override
void initHook() {
super.initHook();
start = DateTime.now();
}
@override
void build(BuildContext context) {}
@override
void dispose() {
print(DateTime.now().difference(start));
super.dispose();
}
}
참고자료
'개발 이야기 > Flutter' 카테고리의 다른 글
Flutter의 Freezed 사용하기 (0) | 2024.06.19 |
---|---|
Flutter의 Hooks 사용하기 02 (0) | 2024.06.18 |
Flutter의 상태관리 02 (0) | 2024.06.16 |
Flutter의 상태관리 01 (0) | 2024.06.15 |
객체지향 프로그래밍 정리 (1) | 2024.06.14 |