해당 문서는 Riverpod 공식 문서에 나오는 watch, read, listen에 대한 설명 중에 나오는 다양한 첨언을 바탕으로 작성된 글입니다.
ref로 provider와 상호작용 하는 방법은 3가지가 있다.
1. ref.watch : 상태의 '취득'과 변화를 구독하여 위젯의 재빌드, 상태값 전달을 동시에 - 가장 많이 쓰이는 방법
2. ref.listen : 상태값이 변경되면 특정 행위를 취해야 할 경우 사용
3. ref.read : 상태값을 '취득'하기만 함 - 콜백함수에 유용
ref.watch 같은 경우에는 콜백함수에 사용 불가능함 : 아래에 설명 있음
위의 노트에서, 기능 구현에는 가급적 ref.watch를 사용하라고 되어 있다. 이는 ref.watch가 리엑티브와 선언형에 가까워지고, 앱을 더 유지보수 하기 쉬워진다는 이유이다. 왜 ref.watch는 Reactive, Declarative 하며 그 특성이 어떻게 앱을 유지보수하기 쉽게 만드는 것일까?
선언형(Declarative) 프로그래밍에 간단히 말하자면, 목표를 명시하고 알고리즘을 명시하지 않는 것이다.
이에 반해 명령형 프로그램은 알고리즘을 명시하고 목표는 명시하지 않는다.
아래의 예시 코드를 한번 살펴보자,
final counter = Ref(0);
counter.watch((previousValue) {
print('Counter value changed from $previousValue to ${counter.value}');
});
counter.value++;
위의 예에서, counter.watch는 counter가 변화하였을 때 어떤 일이 일어나는지(print문)를 명시하고 있다. 이것은 선언적이면서, 반응적(reative) 한데, 변화를 감지하고, ui를 업데이트 하는 작업을 일일히 체크할 필요가 없기 때문이다.
이에 반해, ref.read 와 ref.listen은 변화를 체크하여 ui를 업데이트 하는 작업을 개별적으로 해주어야 한다. 이는 코드 작성에 들이는 시간 및, 오류 발생률을 높이며 결국 유지보수의 효율성을 떨어트린다.
By using ref.watch, you can focus on what should happen when the value changes, without having to worry about the details of how that change is detected or acted upon.
아래의 코드를 한번 살펴보자,
final ref = Ref(0);
ref.watch((previousValue) async {
print('Old value: $previousValue');
await Future.delayed(Duration(seconds: 2));
print('New value: ${ref.value}');
});
ref.value = 1;
ref.value = 2;
이 경우 ref.value를 통해 두번의 watch 콜백이 수행된다.
ref.value = 1을 수행하면
첫번째 프린트문
print('Old value: 1'); 이 실행된 후,
2초간 기다리는 동안
다음 ref.value = 2는 기다리지 않고 다시 콜백으로 처들어간다.
그렇게 되면
print('Old value: 2'); 가 실행된다.
이 다음
print('New value: 2');
print('New value: 2'); 가 실행된다.
final ref = Ref(0);
ref.watch((previousValue) {
print('Old value: $previousValue');
for (int i = 0; i < 1000000000; i++) {
// Do some long-running computation
}
print('New value: ${ref.value}');
});
ref.value = 1;
위의 예처럼, ref.watch이 굉장히 긴 콜백 함수를 실행하여 UI 쓰레드를 오랜 시간동안 막고 있어 결국 앱이 응답하지 않는 결과를 초래하게 될 수 있다.
따라서 ref.watch는 동기적으로, 최대한 빠르고 가벼운 콜백 함수로 구성되어야 한다. 만약 긴 러닝 타임이나 비동기적 처리가 필요하다면, Streams, Future 를 사용하는 것이 좋다.