This is the 29th day of my participation in the August More Text Challenge
This article is translated from MobX’s official Readme document: github.com/mobxjs/mobx…
MobX profile
MobX is a state management framework that makes it easy to bind your application’s responsive data to your UI. This binding is completely automatic and doesn’t feel awkward. MobX allows application developers to focus only on what responsive data the UI consumes, rather than how to keep the two in sync.
There’s nothing magical about the MobX implementation, but some tricks are used to wrap observables, make eye contacts, and automatically track those objects. All aggressions will be restarted once the Observables object is changed. Interestingly, these Reactions can be from anything from console logs to network interface data needed by the UI.
Note: MobX was originally an efficient state management library for JavaScript, and the Dart release page attempts to do the same. For JavaScript versions, go to NPM: JavaScript Version MobX.
The core elements
MobX’s core elements are three: Observables, Actions, and Reactions, as shown below. Next, we introduce the use of each element with a simple Counter Counter application.
Observables
Observables represent the responsive state of the application, which can range from simple objects to complex object trees. If the state of the application is defined as an observables tree, then a reaction-state-tree can be exposed to the UI (or other observers in the application) consuming the state. For counter applications, they can be defined as follows:
import 'package:mobx/mobx.dart';
final counter = Observable(0);
Copy the code
The same is true for complex observables, such as classes.
class Counter {
Counter() {
increment = Action(_increment);
}
final _value = Observable(0);
int get value => _value.value;
set value(int newValue) => _value.value = newValue;
Action increment;
void_increment() { _value.value++; }}Copy the code
This code looks a bit repetitive. So you can use the MobX code generator to simplify writing (*.g.art files are automatically generated by the code generator). To use the code generator, add the builder_runner and mobx_codeGen plug-ins to your development dependencies and run commands in the code directory to generate the corresponding *.g.art files.
flutter packages pub run build_runner build
Copy the code
import 'package:mobx/mobx.dart';
part 'counter.g.dart';
class Counter = CounterBase with _$Counter;
abstract class CounterBase with Store {
@observable
int value = 0;
@action
voidincrement() { value++; }}Copy the code
Note that annotations are used to mark which properties of the class are Observable properties. These annotations are defined in MobX’s code generator. If you want to reduce code, replace the @Observable annotation with the @readonly annotation. After the change, private properties will only provide get methods, not set methods. And users of this state cannot change their values.
In cases where the application state consists of core state and derived state, the core state refers to the state inherent in the business domain being processed. For example, if you have a Contact entity class, firstName and lastName form the core state of Contact. However, fullName is a derived state, obtained by combining firstName and lastName. In this case, something that relies on the core state or other derived state is called Computed Observables (something like a Vue Computed property). This property is also automatically synchronized when the state object on which it depends changes. Such derived objects use the @computed annotation.
import 'package:mobx/mobx.dart';
part 'contact.g.dart';
class Contact = ContactBase with _$Contact;
abstract class ContactBase with Store {
@observable
String firstName;
@observable
String lastName;
@computed
String get fullName => '$firstName.$lastName';
}
Copy the code
Actions
Actions defines how to change the Observables object. Actions make changes more semantic (easier to understand and maintain) than direct changes. For example, calling a increment() action carries more meaning than if value++ were used directly. In addition, Actions can process notifications in batches to ensure changes are not notified until they are complete. This allows the observer to be notified of the completion of the atomic operation based on the action. Note that actions can be nested, in which case they will only be notified when the top-level action is complete.
final counter = Observable(0);
final increment = Action((){
counter.value++;
});
Copy the code
When using actions within a class, you can use the @Action annotation to simplify the code.
/ /...
abstract class CounterBase with Store {
/ /...
@action
voidincrement() { value++; }}Copy the code
For asynchronous operations, MobX handles them automatically, without using the runInAction to wrap them.
@observable
String stuff = ' ';
@observable
loading = false;
@action
Future<void> loadStuff() async {
loading = true; //This notifies observers
stuff = await fetchStuff();
loading = false; //This also notifies observers
}
Copy the code
Reactions
These observables are the end of Mobx observables, Actions, and Reactions. Reactions are observers in a responsive system who are notified when an Observable they are tracking changes. Make eye contact there are several different ways to make eye contact. Either method returns a reaction Disposer method, which will destroy the reaction. A typical feature of These Reactions is to automatically track all Observables without explicitly binding to them. The object is automatically tracked when observables are read in Reaction.
Reactions
One way,autorun
Methods.
import 'package:mobx/mobx.dart';
String greeting = Observable('Hello World');
final dispose = autorun((_){
print(greeting.value);
});
greeting.value = 'Hello MobX';
// Done with the autorun()
dispose();
// Print the result:
// Hello World
// Hello MobX
Copy the code
Reactions
The second way,reaction
Methods:
ReactionDisposer reaction<T>(T Function(Reaction) predicate, void Function(T) effect)
Copy the code
Monitor the Observables object in the Predicate method, and then execute the Effect method when the predicate returns different values. Only the observables in predicate will be traced.
import 'package:mobx/mobx.dart';
String greeting = Observable('Hello World');
final dispose = reaction((_) => greeting.value, (msg) => print(msg));
greeting.value = 'Hello MobX'; // Cause a change
// Done with the reaction()
dispose();
// Print the result:
// Hello MobX
Copy the code
- Make eye contact when all hands are still up to date
ReactionDisposer when(bool Function(Reaction) predicate, void Function() effect)
Copy the code
The effect method is executed only when the predicate method returns true. When the effect method runs, it is automatically destroyed. So it could be a one-off reaction. The reaction can also be destroyed manually in advance.
import 'package:mobx/mobx.dart';
String greeting = Observable('Hello World');
final dispose = when((_) => greeting.value == 'Hello MobX', () = >print('Someone greeted MobX'));
greeting.value = 'Hello MobX'; // Causes a change, runs effect and disposes
// Prints:
// Someone greeted MobX
Copy the code
- Make all these Reactions and Future instead
Future<void> asyncWhen(bool Function(Reaction) predicate)
Copy the code
This is similar to when, except that the result is a Future object — completed when the predicate method returns true. This is handy when waiting for the return value of the predicate method to be true.
final completed = Observable(false);
void waitForCompletion() async {
await asyncWhen(() => _completed.value == true);
print('Completed');
}
Copy the code
Observer
UI is one of the most used visual Reactions for apps. The Observer component (defined in the Flutter_mobx plug-in), in its build method, provides a granular Observer to the Observables object. When the Observables change, the Observer is rebuilt and redrawn.
conclusion
Simple counter code has been uploaded to: MobX state management source. As you can see from the official introduction, MobX is relatively simple to use, and the state management part of the code is relatively easy to write with the help of the code generator. For example, Actions don’t need to be written, and status attributes only need to be annotated. For the parts that can’t be generated, you can actually use VSCode’s code template to do so that the entire code is written efficiently.
This is a column about the entry and practice of Flutter. The corresponding source code is here: Entry and Practice of Flutter.
👍🏻 : feel the harvest please point a praise to encourage!
🌟 : Collect articles, easy to look back!
💬 : Comment exchange, mutual progress!