주요 콘텐츠로 건너뛰기

권장사항(DO/DON'T)

코드의 유지보수성을 높이기 위해 Riverpod를 사용할 때 따라야 할 모범 사례 목록은 다음과 같습니다.

이 목록은 전체 목록이 아니며 변경될 수 있습니다. 제안 사항이 있으면 언제든지 이슈를 열어주세요.

이 목록의 항목은 특별한 순서가 정해져 있지 않습니다.

이러한 권장 사항의 상당 부분은 riverpod_lint로 적용할 수 있습니다. 설치 지침은 시작하기를 참조하세요.

AVOID 위젯에서 provider를 초기화하지 마세요

Providers는 스스로 초기화해야 합니다.
위젯과 같은 외부 요소에 의해 초기화되어서는 안 됩니다.

그렇게 하지 않으면 경합 상태(race conditions) 및 예기치 않은 동작(unexpected behaviors)이 발생할 수 있습니다.

DON'T

class WidgetState extends State<MyWidget> {

void initState() {
super.initState();
// Bad: provider가 자체적으로 초기화해야 합니다.
ref.read(provider).init();
}
}

CONSIDER

이 문제에 대한 "모든 것에 맞는" 해결책은 없습니다.
초기화 로직이 provider 외부의 요인에 따라 달라지는 경우 이러한 로직을 배치하는 올바른 위치는 탐색(navigation)을 트리거하는 버튼의 onPressed 메서드에 있는 경우가 많습니다:

ElevatedButton(
onPressed: () {
ref.read(provider).init();
Navigator.of(context).push(...);
},
child: Text('Navigate'),
)

AVOID 로컬 위젯 상태에 provider를 사용하지 마세요.

Provider는 공유 비즈니스 상태(shared business state)용으로 설계되었습니다. 로컬 위젯 상태와 같은 용도로는 사용하기에 적합하지 않습니다:

  • 양식(form) 상태 저장
  • 현재 선택된 항목
  • 애니메이션
  • 일반적으로 Flutter가 "controller"(예: TextEditingController)로 처리하는 모든 것

로컬 위젯 상태를 처리하는 방법을 찾고 있다면 대신 flutter_hooks를 사용하는 것이 좋습니다.

이를 권장하지 않는 이유 중 하나는 이러한 상태가 경로로 범위가 지정(scoped to a route)되는 경우가 많기 때문입니다.
그렇게 하지 않으면 새 페이지가 이전 페이지의 상태를 재정의하기 때문에 앱의 뒤로 가기 버튼이 손상될 수 있습니다.

DON'T provider를 초기화하는 동안 부가작업(side effects)을 수행하지 마세요.

provider는 일반적으로 "read" 작업을 나타내는 데 사용해야 합니다. 양식 제출과 같은 "write" 작업에는 사용하지 않아야 합니다.

이러한 작업에 provider를 사용하면, 이전에 수행된 부가작업을 건너뛰는 등 예기치 않은 동작이 발생할 수 있습니다.

부가작업의 로딩/오류 상태를 처리하는 방법을 알아보려면 부가 작업 수행(Performing side effects)을 참조하세요.

DON'T:

final submitProvider = FutureProvider((ref) async {
final formState = ref.watch(formState);

// Bad: 공급자는 'write' 작업에 사용해서는 안 됩니다.
return http.post('https://my-api.com', body: formState.toJson());
});

PREFER 정적으로 알려진 providers를 사용하여 ref.watch/read/listen (및 유사한 API)를 호출하세요.

Riverpod은 린트 규칙을 활성화할 것을 강력히 권장합니다(riverpod_lint를 통해).
하지만 린트를 효과적으로 사용하려면 코드를 정적으로 분석할 수 있는 방식으로 작성해야 합니다.

그렇게 하지 않으면 버그를 발견하기가 더 어려워지거나 린트로 오탐이 발생할 수 있습니다.

Do:

final provider = Provider((ref) => 42);

...

// OK provider가 정적으로 알려져 있으므로 확인 가능
ref.watch(provider);

Don't:

class Example extends ConsumerWidget {
Example({required this.provider});
final Provider<int> provider;


Widget build(context, ref) {
// Bad 정적 분석이 `provider`가 무엇인지 알 수 없으므로 나쁨
ref.watch(provider);
}
}

AVOID 동적으로 providers 생성하지 않기

공급자는 최상위 레벨(top-level)의 final 변수만 사용해야 합니다.

Do:

final provider = Provider<String>((ref) => 'Hello world');

Don't:

class Example {
// 지원되지 않는 작업입니다. 메모리 누수 및 예기치 않은 동작이 발생할 수 있습니다.
final provider = Provider<String>((ref) => 'Hello world');
}
정보

provider를 static final 변수로 생성하는 것은 허용되지만, 코드 생성기에서는 지원되지 않습니다.