Saltearse al contenido

Hooks

Los hooks son clases con la capacidad de usar estados y gestionar efectos secundarios. Son un concepto fundamental en Reactter y se utilizan para encapsular la lógica que se puede reutilizar en cualquier parte de la aplicación.

API

Reactter provee los siguientes hooks:

¿Cómo funciona?

Los hooks en Reactter son clases que extienden de RtHook es responsable de vincular el hook a otros hooks y estados, y de gestionar el ciclo de vida del hook.

Los hooks en Reactter son esencialmente entidades con estado porque RtHook hereda de RtState esta herencia permite a los hooks gestionar tanto el estado como los métodos del ciclo de vida de manera eficiente.

Para gestionar estos aspectos, los Hooks proporcionan lo siguiente:

Propiedades y métodos heradados de RtState
  • debugLabel: Una cadena que representa la etiqueta del estado para propósitos de depuración.
  • debugInfo: Un Map que contiene información de depuración sobre el estado.
  • update : Ejecuta una función callback y notifica a sus observadores que el estado ha cambiado. Cuando se invoca, emite dos eventos lifecycle para señalar la transición de estado:
    • Lifecycle.willUpdate se emite primero, indicando la actualización inminente.
    • Lifecycle.didUpdate se emite una vez que el proceso de actualización se ha completado.
  • notify : Fuerza al estado a notificar a sus observadores. A diferencia de update , sólo emite el evento Lifecycle.didUpdate, ya que no implica ningún paso previo a la notificación.
  • bind : Establece una conexión entre el estado y una instancia específica. Esta conexión permite a la instancia actualizarse de forma reactiva en función de los cambios en el estado. Al vincular el estado, la instancia es notificado de los cambios en el estado y puede reflejar adecuadamente esos cambios en su comportamiento.
  • unbind : Libera la conexión entre el estado y la instancia. Al desvincularse, la instancia dejará de recibir actualizaciones del estado. Esto puede ser útil cuando una instancia ya no está utilizando activamente el estado o cuando necesita desvincularse del estado temporal o permanentemente.
  • dispose : Es responsable de limpiar el estado y cualquier observador o recurso asociado. Disponer del estado garantiza que se libere correctamente y que ya no consuma memoria o recursos de procesamiento innecesariamente.

Hook personalizado

Reactter proporciona una forma de crear hooks personalizados para encapsular la lógica que se puede reutilizar en cualquier parte de la aplicación.

Hay varias ventajas en usar hoks personalizados:

  • Reusabilidad: para usar el mismo hook una y otra vez, sin necesidad de escribirlo dos veces.
  • Código limpio: extraer parte del código en un hook proporcionará una base de código más limpia.
  • Mantenibilidad: más fácil de mantener. si necesita cambiar la lógica del hook, solo necesita cambiarla una vez.

Crear un hook personalizado

Para crear un hook personalizado, debes crear una clase que extienda de RtHook y seguir la convención de nomenclatura con el prefijo Use .

Aquí tienes un ejemplo de un hook personalizado que gestiona el estado de un campo de texto:

1
import 'package:reactter/reactter.dart';
2
import 'package:flutter/widgets.dart';
3
4
class UseTextInput extends RtHook {
5
// This line is REQUIRED!
6
final $ = RtHook.$register;
7
8
final controller = TextEditingController();
9
10
String _value = '';
11
String? get value => _value;
12
13
@override
14
initHook() {
15
UseEffect(() {
16
controller.addListener(() {
17
update(() => _value = controller.text);
18
});
19
20
return controller.dispose;
21
}, []);
22
}
23
}

Como se muestra en el ejemplo anterior, podemos utilizar otros hooks como UseEffect para monitorear los cambios en el controlador del campo de texto y asegurarnos de que se elimine cuando el hook se destruya.

El método update se utiliza para establecer el _value interno en el texto actual del controlador, lo que mantiene el estado sincronizado con el campo de texto. Este método es parte de la clase RtHook que te permite actualizar el estado del hook.

Usar un hook personalizado

Los hook personalizados pueden ser llamados desde cualquier parte del código y acceder a su lógica compartida, por ejemplo:

1
import 'package:flutter_reactter/flutter_reactter.dart';
2
import 'use_text_input.dart';
3
4
class MyController {
5
final firstNameInput = UseTextInput();
6
final lastNameInput = UseTextInput();
7
8
late final fullName = Rt.lazyState(
9
() => UseCompute(
10
() {
11
final firstName = firstNameInput.value;
12
final lastName = lastNameInput.value;
13
14
return "$firstName $lastName";
15
},
16
[firstNameInput, lastNameInput],
17
),
18
this,
19
);
20
}
1
import 'package:flutter/material.dart';
2
import 'package:flutter_reactter/flutter_reactter.dart';
3
import 'my_controller.dart';
4
5
class MyCustomForm extends StatelessWidget {
6
const MyCustomForm({Key? key}) : super(key: key);
7
8
@override
9
Widget build(BuildContext context) {
10
return RtProvider<MyController>(
11
() => MyController(),
12
builder: (context, myController, child) {
13
return Scaffold(
14
appBar: AppBar(
15
title: const Text("Custom hook example"),
16
),
17
body: Padding(
18
padding: const EdgeInsets.all(16),
19
child: Column(
20
crossAxisAlignment: CrossAxisAlignment.start,
21
children: [
22
RtConsumer<MyController>(
23
listenStates: (myController) => [myController.fullName],
24
child: const Text(
25
"Full Name:",
26
style: TextStyle(fontWeight: FontWeight.bold),
27
),
28
builder: (context, myController, child) {
29
return Row(
30
children: [
31
child!,
32
const SizedBox(width: 4),
33
Text(myController.fullName.value),
34
],
35
);
36
},
37
),
38
const SizedBox(height: 8),
39
TextField(
40
decoration: InputDecoration(
41
hintText: "First Name",
42
),
43
controller: myController.firstNameInput.controller,
44
),
45
TextField(
46
decoration: InputDecoration(
47
hintText: "Last Name",
48
),
49
controller: myController.lastNameInput.controller,
50
),
51
],
52
),
53
),
54
);
55
},
56
);
57
}
58
}
1
import 'package:reactter/reactter.dart';
2
import 'package:flutter/widgets.dart';
3
4
class UseTextInput extends RtHook {
5
// This line is REQUIRED!
6
final $ = RtHook.$register;
7
8
final controller = TextEditingController();
9
10
String _value = '';
11
String? get value => _value;
12
13
@override
14
initHook() {
15
UseEffect(() {
16
controller.addListener(() {
17
update(() => _value = controller.text);
18
});
19
20
return controller.dispose;
21
}, []);
22
}
23
}
1
import 'package:flutter/material.dart';
2
import 'my_custom_form.dart';
3
4
void main() {
5
runApp(MyApp());
6
}
7
8
class MyApp extends StatelessWidget {
9
@override
10
Widget build(BuildContext context) {
11
return MaterialApp(
12
home: MyCustomForm(),
13
);
14
}
15
}

En el ejemplo anterior, el formulario recopila los datos ingresados en los campos de nombre y apellido, los combina en un nombre completo y muestra el resultado. MyController utiliza el hook UseTextInput (hook personalizado creado previamente) para capturar los valores de nombre y apellido.

El estado fullName se define mediante UseCompute , lo que permite calcular el nombre completo en función de los valores de firstNameInput y lastNameInput. Esto garantiza que el nombre completo se actualice automáticamente cada vez que se modifiquen las entradas