주요 콘텐츠로 건너뛰기

캐시 지우기 및 상태 폐기(disposal)에 반응하기

지금까지 일부 상태(state)를 생성하거나 업데이트하는 방법을 살펴보았습니다. 하지만 언제 상태가 소멸(destruction)되는지에 대해서는 아직 이야기하지 않았습니다.

Riverpod은 상태 폐기(disposal)와 상호작용할 수 있는 다양한 방법을 제공합니다. 여기에는 상태 폐기를 지연(delaying)시키는 것부터 소멸에 반응하는 것까지 다양합니다.

상태는 언제 파괴되며, 그 것을 어떻게 변경하나요?

코드 생성(code-generation)을 사용할 때 기본적으로 provider가 수신이 중지되면 상태가 파괴됩니다.
이는 리스너에 전체 프레임에 대한 활성 리스너가 없을 때 발생합니다. 이 경우 상태가 소멸됩니다.

이 동작은 keepAlive: true를 사용하여 해제(opted out)할 수 있습니다.
이렇게 하면 모든 리스너가 제거될 때 상태가 소멸되는 것을 방지할 수 있습니다.

// 어노테이션에 "keepAlive"를 지정하여 자동 상태 소멸을 비활성화할 수 있습니다.
(keepAlive: true)
int example(ExampleRef ref) {
return 0;
}
노트

자동 폐기를 활성화/비활성화해도 provider가 다시 계산될 때 상태가 소멸되는지 여부에는 영향을 미치지 않습니다.
provider가 다시 계산될 때 상태는 항상 소멸(destroyed)됩니다.

주의

provider가 매개변수를 받을 때 자동 폐기를 활성화하는 것이 좋습니다. 그렇지 않으면 매개변수 조합당 하나의 상태가 생성되어 메모리 누수가 발생할 수 있기 때문입니다.

상태 폐기(disposal)에 대한 반응

Riverpod에는 상태를 파기하는 몇 가지 기본 제공 방법이 있습니다:

  • provider가 더 이상 사용되지 않고 "auto dispose" 모드에 있는 경우(자세한 내용은 나중에 설명). 이 경우 provider와 관련된 모든 상태가 소멸됩니다.
  • ref.watch와 같이 provider가 다시 계산됩니다. 이 경우 이전 상태가 폐기되고 새 상태가 생성됩니다.

두 경우 모두. 이 경우 몇 가지 로직을 실행하고 싶을 수 있습니다.
이는 ref.onDispose로 가능합니다. 이 메서드를 사용하면 상태가 소멸될 때마다 리스너를 등록할 수 있습니다.

예를 들어, 이 메서드를 사용해 활성화된 StreamController를 닫을 수 있습니다:


Stream<int> example(ExampleRef ref) {
final controller = StreamController<int>();

// 상태가 소멸되면 스트림 컨트롤러를 닫습니다.
ref.onDispose(controller.close);

// TO-DO: StreamController에 일부 값을 푸시합니다.
return controller.stream;
}
주의

ref.onDispose의 콜백은 부작용을 유발하지 않아야 합니다. onDispose 내에서 providers를 수정하면 예기치 않은 동작이 발생할 수 있습니다.

정보

다음과 같은 다른 유용한 수명 주기(life-cycles)가 있습니다:

  • provider의 마지막 리스너가 제거될 때 호출되는 ref.onCancel.
  • onCancel이 호출된 후 새 리스너가 추가될 때 호출되는 ref.onResume.
정보

ref.onDispose는 원하는 횟수만큼 호출할 수 있습니다. provider의 Disposable 객체당 한 번씩 호출해도 됩니다. 이렇게 하면 물건을 버리는 것(dispose of something)을 잊어버렸을 때 쉽게 알아차릴 수 있습니다.

ref.invalidate를 사용하여 provider를 수동으로 강제 삭제하기

때로는 provider를 강제로 파괴하고 싶을 때가 있습니다. 다른 provider나 위젯에서 호출할 수 있는 ref.invalidate를 사용하여 수행할 수 있습니다.

ref.invalidate를 사용하면 현재 provider 상태가 파괴됩니다. 그러면 두 가지 결과가 발생할 수 있습니다:

  • provider가 청취되고 있으면 새 상태가 생성됩니다.
  • provider를 청취되고 있지 않으면 provider가 완전히 소멸됩니다.
class MyWidget extends ConsumerWidget {

Widget build(BuildContext context, WidgetRef ref) {
return ElevatedButton(
onPressed: () {
// 클릭 시 provider를 파괴(destroy)합니다.
ref.invalidate(someProvider);
},
child: const Text('dispose a provider'),
);
}
}
정보

provider가 ref.invalidateSelf를 사용하여 스스로 무효화할 수 있습니다. 이 경우 항상 새로운 상태가 생성됩니다.

매개변수를 수신하는 provider를 무효화하려고 할 때, 특정 매개변수 조합 하나를 무효화하거나 모든 매개변수 조합을 한 번에 무효화할 수 있습니다:


String label(LabelRef ref, String userName) {
return 'Hello $userName';
}

// ...

void onTap() {
// 이 provider의 가능한 모든 매개변수 조합을 무효화합니다.
ref.invalidate(labelProvider);
// 특정 조합만 무효화
ref.invalidate(labelProvider('John'));
}

ref.keepAlive를 사용하여 폐기(disposal)를 조정하기

위에서 언급했듯이 자동 폐기를 사용하도록 설정하면 provider에 전체 프레임에 대한 리스너가 없는 경우 상태가 삭제됩니다.

하지만 이 동작을 보다 세밀하게 제어하고 싶을 수도 있습니다. 예를 들어, 성공한 네트워크 요청의 상태는 유지하되 실패한 요청은 캐시하지 않으려 할 수 있습니다.

이는 자동 폐기를 활성화한 후 ref.keepAlive를 사용하면 가능합니다. 이 함수를 사용하면 상태의 자동 폐기를 중지하는 시점을 결정할 수 있습니다.


Future<String> example(ExampleRef ref) async {
final response = await http.get(Uri.parse('https://example.com'));
// 요청이 성공적으로 완료된 후에만 provider를 살아있게(alive) 유지합니다.
// 요청이 실패한(그리고 예외를 throw한) 경우, provider의 수신(listen)이 중단되면 상태가 파괴됩니다.
ref.keepAlive();

// `link`를 사용하여 auto-dispose 동작을 복원(restore)할 수 있습니다:
// link.close();

return response.body;
}
노트

provider가 다시 계산되면 자동 폐기가 다시 활성화됩니다.

ref.keepAlive의 반환 값을 사용하여 자동 폐기로 되돌릴 수도 있습니다.

예: 특정 시간 동안 상태를 살아있게 유지하기

현재 Riverpod은 특정 시간 동안 상태를 유지하는 내장된 방법을 제공하지 않습니다.
하지만 지금까지 살펴본 도구를 사용하면 이러한 기능을 쉽게 구현하고 재사용할 수 있습니다.

Timer + ref.keepAlive를 사용하면 특정 시간 동안 상태를 유지할 수 있습니다. 이 로직을 재사용할 수 있게 하려면 확장 메서드(extension method)로 구현하면 됩니다:

extension CacheForExtension on AutoDisposeRef<Object?> {
/// [duration] 동안 provider를 살아있게 유지합니다.
void cacheFor(Duration duration) {
// 상태가 파괴되는 것을 즉시 방지합니다.
final link = keepAlive();
// 기간이 경과하면 자동 폐기를 다시 활성화합니다.
final timer = Timer(duration, link.close);

// 선택 사항: provider가 다시 계산될 때(예: ref.watch 사용),
// 보류 중인 타이머를 취소합니다.
onDispose(timer.cancel);
}
}

그러면 이렇게 사용할 수 있습니다:


Future<Object> example(ExampleRef ref) async {
/// 5분 동안 상태를 유지합니다.
ref.cacheFor(const Duration(minutes: 5));

return http.get(Uri.https('example.com'));
}

이 로직은 필요에 맞게 조정할 수 있습니다. 예를 들어 ref.onCancel/ref.onResume을 사용하여 특정 시간 동안 provider가 수신 대기하지 않은 경우에만 상태를 삭제할 수 있습니다.