หลักการออกแบบเบื้องหลัง miniriverpod.
แพ็กเกจนี้จงใจลดทอนฟีเจอร์เพื่อให้พฤติกรรมชัดเจน: เอกลักษณ์ของ provider ด้วย args, การ inject แบบ scoped และ semantics ของการ dispose ที่คาดเดาได้.
สิ่งที่เปลี่ยนไปจาก Riverpod
แทนที่จะใช้ generated family classes และ channels ของ notifier แบบ implicit, miniriverpod เลือก subclass + args + explicit invoke.
Provider identity
runtimeType + args hash
family alternative
สืบทอด Provider / AsyncProvider และส่ง super.args((...))
DI fallback
Scope<T>.required + overrideWithValue
ทำไมสิ่งนี้จึงสำคัญ
คุณสามารถใช้ constructor ของ Dart ปกติเพื่อให้เหตุผลเกี่ยวกับ equality และ overrides ได้ ซึ่งทำให้การ debug และการทดสอบตรงไปตรงมา.
การระบุ Provider ด้วย args
args กำหนดคีย์ของ provider ดังนั้น args ที่เท่ากันหมายถึง cache entry เดียวกันภายใน ProviderContainer.
Identity rule
ผลลัพธ์เชิงปฏิบัติ
- ไม่จำเป็นต้องมี family type แยกต่างหาก.
- การ override ราย argument ทำได้โดยการสร้าง provider instances.
- ควรรักษา args ให้คงที่และไม่เปลี่ยนแปลงเพื่อ caching ที่คาดเดาได้.
ตัวอย่าง: provider แบบ family + Scope สำรอง
ใช้ argument ของ constructor เป็นตัวระบุ และ inject instance สำรองผ่าน Scope.
class ProductProvider extends AsyncProvider<List<Product>> {
ProductProvider({this.search = ''}) : super.args((search,));
final String search;
static final fallback = Scope<ProductProvider>.required('product.fallback');
@override
FutureOr<List<Product>> build(ref) async {
final api = ref.watch(productsApiProvider);
return api.search(q: search);
}
}
// Inject
ProviderScope(
overrides: [
ProductProvider.fallback.overrideWithValue(ProductProvider(search: 'jeans')),
],
child: const App(),
);
ขั้นตอนถัดไป
Providers และการอ่าน
ดูรูปแบบที่เป็นรูปธรรมสำหรับ watch/read/listen และ AsyncProvider.future.
เปิด Providers