Provider/widget yeniden oluşturmaları nasıl azaltılır
Şimdiye kadar gördüklerimizle tamamen çalışan bir uygulama yazabilecek durumdayız. Yine de performans konusunda aklınıza bazı sorular takılmış olabilir.
Bu sayfada, kodunuzu optimize etmenize yardımcı olabilecek birkaç ipucuna değineceğiz.
Herhangi bir optimizasyon yapmadan önce uygulamanızı mutlaka ölçümleyin. Optimizasyonların getirdiği ek karmaşıklık, elde edilen küçük kazanımlara değmeyebilir.
"select" ile widget/provider yeniden oluşturmalarını filtreleme
Fark etmiş olabileceğiniz gibi, ref.watch varsayılan olarak bir nesnenin
herhangi bir özelliği değiştiğinde consumer'ların/provider'ların yeniden
oluşturulmasına neden olur.
Örneğin, bir User dinleyip yalnızca "name" özelliğini kullansanız bile,
"age" değiştiğinde consumer yeniden oluşturulur.
Ancak bir consumer özelliklerin yalnızca bir kısmını kullanıyorsa, diğer özellikler değiştiğinde widget'ın yeniden oluşturulmasını istemezsiniz.
Bunu provider'ların select özelliğini kullanarak sağlayabilirsiniz.
Bu durumda ref.watch artık nesnenin tamamını değil, yalnızca seçilen
özellikleri döndürür.
Ve consumer'larınız/provider'larınız yalnızca bu seçilen özellikler
değiştiğinde yeniden oluşturulur.
- riverpod
- riverpod_generator
class User {
late String firstName, lastName;
}
final provider = Provider(
(ref) => User()
..firstName = 'John'
..lastName = 'Doe',
);
class ConsumerExample extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// Şunu yazmak yerine:
// String name = ref.watch(provider).firstName!;
// Şöyle yazabiliriz:
String name = ref.watch(provider.select((it) => it.firstName));
// Bu, widget'ın yalnızca "firstName" üzerindeki değişiklikleri dinlemesini sağlar.
return Text('Hello $name');
}
}
class User {
late String firstName, lastName;
}
User example(Ref ref) => User()
..firstName = 'John'
..lastName = 'Doe';
class ConsumerExample extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// Şunu yazmak yerine:
// String name = ref.watch(provider).firstName!;
// Şöyle yazabiliriz:
String name = ref.watch(exampleProvider.select((it) => it.firstName));
// Bu, widget'ın yalnızca "firstName" üzerindeki değişiklikleri dinlemesini sağlar.
return Text('Hello $name');
}
}
selecti istediğiniz kadar çağırabilirsiniz.
İhtiyaç duyduğunuz her özellik için bir kez çağırmakta serbestsiniz.
Seçilen özelliklerin değişmez (immutable) olması beklenir.
Bir List döndürüp ardından o listeyi değiştirmek yeniden oluşturmayı tetiklemez.
select kullanmak tekil okuma işlemlerini bir miktar yavaşlatır ve kodunuzun
karmaşıklığını çok az da olsa artırır.
Söz konusu "diğer özellikler" nadiren değişiyorsa kullanmaya değmeyebilir.
Asenkron özellikleri seçme
Başka bir provider'ı dinleyen bir provider'ı optimize etmeye çalışıyorsanız, büyük ihtimalle o diğer provider asenkrondur.
Normalde değeri almak için ref.watch(anotherProvider.future) yazardınız.
Sorun şu ki select, await edemeyeceğiniz bir AsyncValue üzerinde uygulanır.
Bunun için selectAsync kullanabilirsiniz. Yalnızca asenkron koda özgüdür ve
bir provider'ın yaydığı veri üzerinde select işlemi yapmanızı sağlar.
Kullanımı select ile benzerdir, ancak bunun yerine bir Future döndürür:
- riverpod
- riverpod_generator
final provider = FutureProvider((ref) async {
// Bir kullanıcı hazır olana kadar bekleyin ve yalnızca "firstName" özelliğini dinleyin
final firstName = await ref.watch(
userProvider.selectAsync((it) => it.firstName),
);
// TODO "firstName" değerini başka bir şeyi getirmek için kullanın
});
Object? example(Ref ref) async {
// Bir kullanıcı hazır olana kadar bekleyin ve yalnızca "firstName" özelliğini dinleyin
final firstName = await ref.watch(
userProvider.selectAsync((it) => it.firstName),
);
// TODO "firstName" değerini başka bir şeyi getirmek için kullanın
}