Actualizar objectos en estado
Un estado de Reactter( RtState
) como Signal
, UseState
, UseAsyncState
, UseReducer
y UseCompute
pueden contener cualquier tipo de valor de Dart, incluidos objetos.
Sin embargo, no debes modificar los objetos contenidos en el estado de Reactter directamente.
En esta guía, aprenderás cómo actualizar objetos de forma segura y efectiva en un estado Reactter.
¿Qué es una mutación?
Puedes almacenar cualquier tipo de valor de Dart en el estado.
final count = Signal(0);
Hasta ahora has estado trabajando con números, cadenas y booleanos. Estos tipos de valores de Dart son inmutables, lo que significa que no se pueden cambiar o son de solo lectura. Puedes desencadenar una nueva representación para reemplazar un valor:
count.value = 2;
El estado count
cambió de 0
a 2
, pero el número 0
en sí mismo no cambió.
No es posible realizar cambios en los valores primitivos incorporados como números, cadenas y booleanos en Dart.
Ahora considera un objeto en un estado:
7 collapsed lines
class User { String name; int age;
User({required this.name, required this.age});}
final user = Signal(User(name: "Jane Doe", age: 25));
Tecnicamente, es posible cambiar el contenido del objeto en sí. Esto se llama una mutación:
user.value.name = "John Doe";
Sin embargo, aunque los objetos en el estado de Reactter son técnicamente mutables, debes tratarlos como si fueran inmutables, como números, booleanos y cadenas. En lugar de mutarlos, siempre debes reemplazarlos.
Tratar el estado como de solo lectura
En otras palabras, debes tratar cualquier objeto de Dart que pongas en el estado como de solo lectura.
El siguiente ejemplo mantiene un objeto en el estado para representar al usuario.
El nombre del usuario se cambia de "Jane Doe"
a "John Doe"
cuando haces clic en el botón,
pero el nombre del usuario sigue siendo el mismo.
1import 'package:flutter/material.dart';2import 'package:reactter/reactter.dart';3
4void main() {5 runApp(MyApp());6}7
8class User {9 String name;10 int age;11
12 User({required this.name, required this.age});13}14
15final user = Signal(User(name: "Jane Doe", age: 25));16
17class MyApp extends StatelessWidget {18 @override19 Widget build(BuildContext context) {20 return MaterialApp(21 home: Scaffold(22 appBar: AppBar(title: Text("Inmutable state example")),23 body: Center(24 child: RtSignalWatcher((){25 return Column(26 children: [27 Text('Name: ${user.value.name}'),28 Text('Age: ${user.value.age}'),29 ElevatedButton(30 onPressed: () {31 user.value.name = "John Doe";32 },33 child: Text("Change Name"),34 ),35 ],36 );37 }),38 ),39 ),40 );41 }42}
El problema está en este fragmento de código.
24user.value.name = "John Doe";
Este código modifica el objeto asignado a un nuevo name
cuando se hace clic en el botón
pero no desencadena un nuevo renderizado porque el objeto en sí no ha cambiado.
La propiedad name
del objeto ha cambiado, pero el objeto en sí no lo ha hecho.
Y Reactter no sabe que el objeto ha cambiado porque sigue siendo el mismo objeto.
Aunque la mutación del estado puede funcionar en algunos casos, no lo recomendamos.
Debes tratar el valor de estado al que tienes acceso como de solo lectura.
Para desencadenar un nuevo renderizado en este caso, crea un nuevo objeto y pásalo a la función de configuración del estado:
24user.value = User(name: "John Doe", age: user.value.age);
Cunado value
se establece en un nuevo objeto, Reactter sabe que el estado ha cambiado y desencadena un nuevo renderizado.
Copiar objetos
En el ejemplo anterior, creaste un nuevo objeto con el mismo age
y un name
diferente.
Pero ¿qué pasa si quieres cambiar el age
y mantener el name
igual?. También puedes hacerlo:
user.value = User(name: user.value.name, age: 30);
Esto es un patrón común cuando trabajas con objetos en el estado.
Sin embargo, crear un nuevo objeto cada vez que quieras cambiar una propiedad puede ser tedioso.
Para simplificar este proceso, puedes agregar el siguiente método( copyWith
) a la clase User
:
1class User {5 collapsed lines
2 final String name;3 final int age;4
5 const User({required this.name, required this.age});6
7 User copyWith({String? name, int? age}) {8 return User(9 name: name ?? this.name,10 age: age ?? this.age,11 );12 }13}
Este método crea un nuevo objeto con las mismas propiedades que el objeto original, excepto por las propiedades que especifiques.
Puedes usar este método para crear un nuevo objeto con el mismo name
y un age
diferente:
user.value = user.value.copyWith(age: 30);
Resumen
- Trata los objetos en el estado como de solo lectura.
- Cuando necesites actualizar un objeto, crea uno nuevo o haz una copia del objeto existente, y luego establece el estado para usar este nuevo o copiado objeto.
- Evita mutar objetos en el estado directamente.
- Crea un nuevo objeto y pásalo a la función de configuración del estado para desencadenar un nuevo renderizado.
- Usa el método
copyWith
para crear un nuevo objeto con las mismas propiedades que el objeto original, excepto por las propiedades que especifiques.