riverpod read listen
| topics | |
| types | 이론 실습 |
| tags | |
| references | anpigon.tistory.com/359 riverpod.dev/docs/essentials/reading |
Riverpod read vs watch vs listen
Riverpod에서 Provider 값을 읽는 세 가지 방법의 차이다.
핵심 차이
| 메소드 | 구독 | 리빌드 | 사용 시점 |
|---|---|---|---|
watch |
O | O | UI에 상태 반영 |
read |
X | X | 이벤트 핸들러 (일회성) |
listen |
O | X | 사이드 이펙트 처리 |
watch - UI 구독
상태가 변경될 때마다 위젯을 리빌드한다.
class CounterScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 상태 변경 시 위젯 리빌드
final count = ref.watch(counterProvider);
return Text('Count: $count');
}
}
언제 쓸까: UI에 상태를 표시할 때. 상태가 바뀌면 화면도 바뀌어야 할 때.
watch 주의사항
// 나쁜 예 - 이벤트 핸들러에서 watch
onPressed: () {
final count = ref.watch(counterProvider); // 빌드 중이 아닌데 watch 사용
}
// 좋은 예
onPressed: () {
final count = ref.read(counterProvider);
}
read - 일회성 읽기
구독하지 않고 현재 값만 읽는다. 리빌드 없음.
class CounterScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return ElevatedButton(
onPressed: () {
// 일회성으로 값 읽기
final count = ref.read(counterProvider);
print('Current: $count');
// notifier 접근
ref.read(counterProvider.notifier).increment();
},
child: Text('Increment'),
);
}
}
언제 쓸까: 버튼 클릭, 제스처 등 이벤트 핸들러에서. 상태를 변경할 때.
read 주의사항
// 나쁜 예 - build에서 read
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.read(counterProvider); // 상태 변경 감지 안됨!
return Text('Count: $count');
}
// 좋은 예
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Text('Count: $count');
}
listen - 사이드 이펙트
상태 변경을 구독하지만 리빌드하지 않고 콜백만 실행한다.
class CounterScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 상태 변경 시 콜백 실행 (리빌드 X)
ref.listen<int>(counterProvider, (previous, next) {
if (next >= 10) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('10 reached!')),
);
}
});
final count = ref.watch(counterProvider);
return Text('Count: $count');
}
}
언제 쓸까: 스낵바, 다이얼로그, 네비게이션 등 사이드 이펙트를 실행할 때.
listen 활용 예시
// 에러 발생 시 스낵바 표시
ref.listen<AsyncValue<User>>(userProvider, (_, next) {
next.whenOrNull(
error: (error, _) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $error')),
);
},
);
});
// 로그인 성공 시 화면 이동
ref.listen<AuthState>(authProvider, (_, next) {
if (next.isLoggedIn) {
context.go('/home');
}
});
정리
Widget build(BuildContext context, WidgetRef ref) {
// UI에 표시 → watch
final user = ref.watch(userProvider);
// 사이드 이펙트 → listen
ref.listen(errorProvider, (_, error) {
showSnackBar(error);
});
return Column(
children: [
Text(user.name),
ElevatedButton(
onPressed: () {
// 이벤트 핸들러 → read
ref.read(userProvider.notifier).logout();
},
child: Text('Logout'),
),
],
);
}
관련 문서
- riverpod annotation - Riverpod 어노테이션
- AsyncValue vs AsyncData - 비동기 상태 처리
- flutter future - Future 비동기 처리
- gorouter 기본사용법 - 라우팅과 상태 연동