Skip to content

Commit

Permalink
doc: Update example code of lifecycle and hooks to show as tabs
Browse files Browse the repository at this point in the history
  • Loading branch information
CarLeonDev committed Jun 24, 2024
1 parent 1e4cacb commit b050c77
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 52 deletions.
82 changes: 40 additions & 42 deletions website/src/content/docs/core_concepts/hooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@ sidebar:
order: 6
---
import { HE, HM, HN, HT } from '@/components/Highlight';
import { Code } from "@astrojs/starlight/components";
import { Tabs, TabItem } from "@/components/Tabs";
import ZappButton from "@/components/ZappButton.astro";
import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx';
import { marks } from '@/examples/marks.ts';

import MainCode from '@/examples/custom_hook/lib/main.dart?raw';
import MyCustomFormCode from '@/examples/custom_hook/lib/my_custom_form.dart?raw';
import MyControllerCode from '@/examples/custom_hook/lib/my_controller.dart?raw';
import UseTextInputCode from '@/examples/custom_hook/lib/use_text_input.dart?raw';

Hooks are classes with the ability to use states and manage side effects. They are a fundamental concept in Reactter and are used to encapsulate logic that can be reused across the application.

Expand All @@ -27,7 +36,6 @@ Hooks in Reactter are essentially stateful entities because <HT>`ReactterHook`</
To manage these aspects, Hooks provide the following:
<StateMethodsLite/>


## Custom hook

Reactter provides a way to create _**Custom Hooks**_ to encapsulate logic that can be reused across the application.
Expand All @@ -38,31 +46,17 @@ There are several advantages to using _**Custom Hooks**_:
- **Clean Code**: extracting part of code into a hook will provide a cleaner codebase.
- **Maintainability**: easier to maintain. if you need to change the logic of the hook, you only need to change it once.


### Creating a custom hook

To create a _**Custom hook**_, you need to create a class that extends <HT>`ReactterHook`</HT> and follow the naming convention with the <HT>`Use`</HT> prefix.

Here's an example of a _**Custom Hook**_ that increments and decrements a value:

```dart title="use_count.dart" "ReactterHook" "ReactterHook.$register" {"* This line is REQUIRED!!":4-5} "update" "_count"
import 'package:reactter/reactter.dart';
class UseCount extends ReactterHook {
final $ = ReactterHook.$register;
int _count = 0;
int get value => _count;
Here's an example of a _**Custom Hook**_ that manages the state of a text input:

UseCount(int initial) : _count = initial;
<Code code={UseTextInputCode} lang="dart" mark={[...marks, {range: "5-6", label: "* This line is REQUIRED! "}]} />

void increment() => update(() => _count += 1);
void decrement() => update(() => _count -= 1);
}
```
As shown in the example above, we can utilize other hooks such as <HT>[`UseEffect`](/reactter/hooks/use_effect)</HT> to monitor changes in the text input's controller and ensure it is disposed when the hook is destroyed.

In above example, we have used the <HM>`update`</HM> method to update the value of the `_count` variable.
The <HM>`update`</HM> method is used to set the internal `_value` to the current text in the controller, which keeps the state synchronized with the input.
This methods is a part of the <HT>`ReactterHook`</HT> class that allows you to update the state of the hook.

:::note
Expand All @@ -86,27 +80,31 @@ To avoid this, it's recommended to place this line as the first line in the clas

You can then call that _**Custom Hook**_ from anywhere in the code and get access to its shared logic:

```dart title="my_controller.dart" "Reactter.on" "Lifecycle.didUpdate" "UseCount" /count(?! )(?!.dart)/ "count.value" "count.increment"
import 'dart:async';
import 'package:reactter/reactter.dart';
import 'use_count.dart';
class MyController {
final count = UseCount(0);
MyController() {
Timer.periodic(Duration(seconds: 1), (_) => count.increment());
// Print count value every second
Reactter.on(
count,
Lifecycle.didUpdate,
(_, __) => print("Count: ${count.value}",
);
}
}
```
<div class="code-tabs">
<ZappButton path="examples/custom_hook"/>

<Tabs>
<TabItem>
<HM single slot="label">my_controller.dart</HM>
<Code code={MyControllerCode} lang="dart" mark={[...marks, {range: "5-6"}, 11, 12]}/>
</TabItem>

<TabItem>
<HM single slot="label">my_custom_form.dart</HM>
<Code code={MyCustomFormCode} lang="dart" mark={[...marks, 43, 49]}/>
</TabItem>

<TabItem label="use_text_input.dart">
<Code code={UseTextInputCode} lang="dart" mark={[...marks]}/>
</TabItem>

<TabItem label="main.dart">
<Code code={MainCode} lang="dart" mark={marks} />
</TabItem>
</Tabs>
</div>

In the example above, the form captures first and last name inputs, combines them into a full name, and displays the result.
<HT>`MyController`</HT> uses <HT>`UseTextInput`</HT> hook for capturing the first and last name.
The `fullName` state is defined using <HT>[`UseCompute`](/reactter/hooks/use_compute)</HT> to compute the full name based on the values of `firstNameInput` and `lastNameInput`. This ensures the full name is automatically updated whenever either input changes.

In this example, the <HT>`MyController`</HT> class uses the <HT>`UseCount`</HT> custom hook to manage the state of the `count` variable.
The `count` variable is initialized with an initial value of <HN>`0`</HN> and is incremented by <HN>`1`</HN> every second using a timer.
The <HE>`Lifecycle.didUpdate`</HE> event of the `count` state is subscribed to, and the current value of the `count` state is printed every second.
2 changes: 1 addition & 1 deletion website/src/content/docs/core_concepts/lifecycle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { Code } from "@astrojs/starlight/components";
import { Tabs, TabItem } from "@/components/Tabs";
import ZappButton from "@/components/ZappButton.astro";
import { marks } from '@/examples/marks.ts'

import TipLifecycleExample from '@/content/docs/shareds/tip_lifecycle_example.mdx';
import { counterViewMark } from '@/examples/counter/marks.ts';

import counterControllerEventHandlerCode from '@/examples/lifecycle_event_handler/lib/counter_controller.dart?raw';
import counterEventHandlerCode from '@/examples/lifecycle_event_handler/lib/counter.dart?raw';
Expand Down
9 changes: 0 additions & 9 deletions website/src/examples/counter/marks.ts

This file was deleted.

15 changes: 15 additions & 0 deletions website/src/examples/custom_hook/lib/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
import 'my_custom_form.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyCustomForm(),
);
}
}
20 changes: 20 additions & 0 deletions website/src/examples/custom_hook/lib/my_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:flutter_reactter/flutter_reactter.dart';
import 'use_text_input.dart';

class MyController {
final firstNameInput = UseTextInput();
final lastNameInput = UseTextInput();

late final fullName = Reactter.lazyState(
() => UseCompute(
() {
final firstName = firstNameInput.value;
final lastName = lastNameInput.value;

return "$firstName $lastName";
},
[firstNameInput, lastNameInput],
),
this,
);
}
58 changes: 58 additions & 0 deletions website/src/examples/custom_hook/lib/my_custom_form.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import 'package:flutter_reactter/flutter_reactter.dart';
import 'my_controller.dart';

class MyCustomForm extends StatelessWidget {
const MyCustomForm({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return ReactterProvider<MyController>(
() => MyController(),
builder: (context, myController, child) {
return Scaffold(
appBar: AppBar(
title: const Text("Retrieve Text Input"),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ReactterConsumer<MyController>(
listenStates: (myController) => [myController.fullName],
child: const Text(
"Full Name:",
style: TextStyle(fontWeight: FontWeight.bold),
),
builder: (context, myController, child) {
return Row(
children: [
child!,
const SizedBox(width: 4),
Text(myController.fullName.value),
],
);
},
),
const SizedBox(height: 8),
TextField(
decoration: InputDecoration(
hintText: "First Name",
),
controller: myController.firstNameInput.controller,
),
TextField(
decoration: InputDecoration(
hintText: "Last Name",
),
controller: myController.lastNameInput.controller,
),
],
),
),
);
},
);
}
}
22 changes: 22 additions & 0 deletions website/src/examples/custom_hook/lib/use_text_input.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:reactter/reactter.dart';
import 'package:flutter/widgets.dart';

class UseTextInput extends ReactterHook {
// This line is REQUIRED!
final $ = ReactterHook.$register;

final controller = TextEditingController();

String _value = '';
String? get value => _value;

UseTextInput() {
UseEffect(() {
controller.addListener(() {
update(() => _value = controller.text);
});

return controller.dispose;
}, []);
}
}
14 changes: 14 additions & 0 deletions website/src/examples/custom_hook/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: MyCustomHook
description: A simple counter example
version: 0.1.0

environment:
sdk: ">=2.19.0 <4.0.0"

dependencies:
flutter:
sdk: flutter
flutter_reactter: ^7.1.0

flutter:
uses-material-design: true
1 change: 1 addition & 0 deletions website/src/examples/marks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const marks = [
"Reactter.emit",
"Reactter.off",
"Reactter.offAll",
"ReactterHook",
"ReactterState",
"ReactterDependency",
"ReactterDependencyMode",
Expand Down

0 comments on commit b050c77

Please sign in to comment.