Forcing the MobX state to be changed through declared actions only in the Flutter framework - flutter

I'm experimenting with MobX state management solution in Flutter. I really like its conciseness and its simplicity but I'm a bit concerned in the drawbacks its way of mutating the state can introduce.
Theoretically , we can force the state to be mutated only using actions and this is great because we can have a history of the changes using the spy feature the package provides.
However, actions are automatically created by the framework if we do not use a specific one. For example, in the code below, I created a counter store class and I used it in the main function to assign a new value for the counter without using a predeclared action. The stric-mode has been previously set to "always".
abstract class _Counter with Store {
#observable
int value = 0;
#action
void increment() {
value++;
}
}
final counter = Counter(); // Instantiate the store
void main() {
mainContext.config = mainContext.config
.clone(isSpyEnabled: true, writePolicy: ReactiveWritePolicy.always);
mainContext.spy((event) {
print("event name : " + event.name);
print("event type : " + event.type);
});
Counter counter = Counter();
counter.value = 10;
}
I was expecting an error to raise saying that it is not possible to change the state outside of action but this does not happen because an ad hoc action is created , called value_set, automatically. I'm wondering ,then, which is the point of forcing to use actions if it is possible to avoid using them. I mean, if we directly change the state, an action is created automatically and seen by the spy functionality making all the process more robust and predictable but, what if I want the state to be changed only by actions I previously implemented? Is there a way of doing so? For example, in the counter code I just provided , is there a way of making the counter incrementalbe only by 1? Because, in the way Mobx works, I could write everywhere in the code something like
counter.value = 10
and this will work just fine without any problems. Forcing people to use preset actions could increase the predicatability a lot and facilitate the teamwork too I think.
I tried to make the value variable private but it still remains accessible from the outside , also if annotated with #readonly.

Hm, that looks weird for me too, because it does work differently in MobX JS (exactly like you describing), but it seems that Dart MobX changed behaviour for single field values and they are automatically wrapped in actions now, yes.
Maybe there should be a optional rule to turn strict checking on again, it would make sense. I suggest you to create and issue or discussion on Dart MobX Github.
More info there: https://github.com/mobxjs/mobx.dart/issues/206

At the end, I found out that the behaviour I was looking for was obtainable with the #readonly annotation. Marking a variable with #readonly annotation avoids automatically creating its setter and getter methods. Moreover it requires the annotated variable to be private, denying the variable to be set directly.
Note: By the way, the way the Dart language is implemented, it is necessary to locate the Counter in a separate file to provide the desired behaviour, otherwise its private fields would be accessible anyway.

Related

Changing and accessing global var in flutter/dart

I am very new to Dart/Flutter, but I am trying to learn and understand. Here is my issue:
I got global.dart that stores global vars and I know how to access and to change them, but I want a separate class to update itself when a global var is changed from a different class. These classes are stored in different files.
I am not sure if any code is needed in this question..
Based on your requirement, what you need is called ChangeNotifier and you can use a Provider to provide the variables of the ChangeNotifier in any file they are required.
Before using the ChangeNotifiers (can be multiple, like NewsProvider, FeedProvider, CommentProvider etc), you've to inject it into the widget cycle before the Widget/Component you intend to use it in. For that, check the widget Provider or MultiProvider in the Provider linked above.
After setting up the ChangeNotifiers and injecting them into the widget cycle, you can use them in any widget or class as:
For one-time reading the value, use read as:
SampleNotifier sample = context.read<SampleNotifier>();
print(sample.variableName);
Also, in onClick triggered functions, you can't use watch, only read is allowed, as these functions are static in nature and doesn't require dynamically updated value. Also, when you want to access and call a function declared in a ChangeNotifier, for example the setSignIn mentioned below, use read.
For listening to the changes in the variables of the provider, use watch as:
SampleNotifier sample = context.watch<SampleNotifier>();
print(sample.variableName);
Above mentioned read and watch are not the actual functions provided by the package Provider but their short form as extension function on context and you don't need both, this one's enough and better.
A sample ChangeNotifer:
class SampleNotifier extends ChangeNotifier {
//A locally-scoped sample variable
bool _isUserLoggedIn = false;
//Getter to get the sample value
bool get isUserLoggedIn => _isUserLoggedIn;
//Contructor sample to initialize the variables with the default values
//You can initialize it with the global values in your global vars file
FabNotifier() {
_isUserLoggedIn = false;
}
//Sample function to perform some logic and set the updated value
setSignIn() {
_isUserLoggedIn = true;
//To notify all the classes listening to this variable, to update the usage there
//watch is used to listen to the value, so wherever watch is used, it will update the value
notifyListeners();
}
}
To inject this SampleNotifier, check the Provider/MultiProvider in the linked Provide package above and to use it, check the read or watch mentioned above.

How to copy a Map in dart without getting Reference of the old one

I have a map that i get from my service and i want to copy it into 2 new Maps without being referenced and every time i change a value into one, value change to other too.
This is the Initial Map from my service config.UserGroups and i copy it like this
SavedSettings.UserGroups = new Map.from(config.UserGroups);
UnSavedSettings.UserGroups = new Map.from(config.UserGroups);
This Map is dynamic but it has String,object
Do we have an easy way to bypass reference;
What you are asking for is called deep copying and is something you need to implement yourself for your data structures since List and Map just contains references to objects. So when you are making a copy of a List or Map, you are copying the references and not the objects inside the collection.
So the correct solution would be to make some logic which allows you to make copies of UserGroup objects and put these into your new list.
But.... if you are not scared of hacky solutions....
Section with hacky solution
If you really want to have some way to get deep copy from the Dart language (and don't care if the solution is kinda hacky) it is in theory possible by make use of the idea that we can send objects to isolates which are deep copied if possible.
So we can create an ReceivePort and send our object to our self. An example of this can be seen in this example:
class MyObject {
int value;
MyObject(this.value);
#override
String toString() => 'MyObject($value)';
}
Future<void> main() async {
final list1 = [MyObject(5)];
final list2 = await hackyDeepCopy(list1);
list2[0].value = 10;
print(list1); // [MyObject(5)]
print(list2); // [MyObject(10)]
}
Future<T> hackyDeepCopy<T>(T object) async =>
await (ReceivePort()..sendPort.send(object)).first as T;
This "solution"/hack comes with some limitations when it comes to data we can copy:
The content of message can be:
Null
bool
int
double
String
List or Map (whose elements are any of these)
TransferableTypedData
SendPort
Capability
In the special circumstances when two isolates share the same code and
are running in the same process (e.g. isolates created via
Isolate.spawn), it is also possible to send object instances (which
would be copied in the process). This is currently only supported by
the Dart Native platform.
https://api.dart.dev/stable/2.14.4/dart-isolate/SendPort/send.html
It is the "In the special circumstances when two isolates share the same code and are running in the same process" part we makes use of in hackyDeepCopy which allows us to also deep copy custom made objects.
Another limitation is that hackyDeepCopy needs to be async which is kinda annoying if your code is not already async.
And lastly, this only really works on native platforms (so no Dart-running-as JavaScript stuff).

Dart - Snackbar service - global function vs static class function vs singleton

I would like to create a wrapper around a third-party package Snackbar (the third party Snackbar is simply called like this: Get.snackbar(title, body, snackPosition: SnackPosition.BOTTOM);) in order to decouple my app from the package and be able to easily swap in another Snackbar implementation.
I figure it should be a singleton for performance reasons. I would like to call it like VpSnackBar(title, body); ideally. I found how to make a singleton in dart (Vp is my app prefix to identify it was made in my app):
Here I have created this singleton
class VpSnackBar {
VpSnackBar._privateConstructor();
static final VpSnackBar instance = VpSnackBar._privateConstructor();
static void show(String title, String body) {
Get.snackbar(title, body, snackPosition: SnackPosition.BOTTOM);
}
}
I would have to call it like VpSnackBar.show(title, body);. But then I'm like should I just make a plain class with a static method which would do what this does, without ever being instantiated? Is instantiation beneficial?
Another option is a global function with an uppercase name so it is used like a class:
void VpSnackBar(String title, String body) {
Get.snackbar(title, body, snackPosition: SnackPosition.BOTTOM);
}
This is the only way that I know how to call it like VpSnackBar(title, body);. Which is my preferred way to call it. However, is a global function a bad practice? Could I achieve calling it like VpSnackBar(title, body); without a global function? Which way is the best practice?
So that I can be safe against any potential changes to the package
You won't be in any sort of way.
If the package owner decides to completely change the method internals without touching its prototype, you'll still be affected by those changes.
If the package owner decides to change the method's prototype in a backwards incompatible way, which features a breaking change, you'll also be affected by those changes.
I figure it should be a singleton for performance reasons.
I see that you're not running any heavy processes (like IO) when instantiating your class, so I cannot see why you should be concerned about performance. Consider that, for each widget build() call, which happens a lot of times in one second, hundreds of classes get instantiated, and they do not affect app's performance.
However, is a global function a bad practice?
Not at all. The actual problem with your approach is capitalizing the function's first letter. This is a bad practice, since it leaves who's reading to believe that your function call is actually a constructor call.
Which way is the best practice?
As I stated, I cannot even see a reasonable motive to wrap snackbar in the first place. But leaving this matter apart, each of your solutions seem equally fair to me (as long as you the uncapitalize the first of the function). In such situation, you may want to use the one that you feel more comfortable with.

Composing IObservables and cleaning up after registrations

I have some code in a class that takes FileSystemWatcher events and flattens them into an event in my domain:
(Please note, the *AsObservable methods are extensions from elsewhere in my project, they do what they say 🙂.)
watcher = new FileSystemWatcher(ConfigurationFilePath);
ChangeObservable = Observable
.Merge(
watcher.ChangedAsObservable().Select((args) =>
{
return new ConfigurationChangedArgs
{
Type = ConfigurationChangeType.Edited,
};
}),
watcher.DeletedAsObservable().Select((args) =>
{
return new ConfigurationChangedArgs
{
Type = ConfigurationChangeType.Deleted,
};
}),
watcher.RenamedAsObservable().Select((args) =>
{
return new ConfigurationChangedArgs
{
Type = ConfigurationChangeType.Renamed,
};
})
);
ChangeObservable.Subscribe((args) =>
{
Changed.Invoke(this, args);
});
Something that I'm trying to wrap my head around as I'm learning are best practices around naming, ownership and cleanup of the IObservable and IDisposable returned by code like this.
So, some specific questions:
Is it okay to leak IObservables from a class that creates them? For example, is the property I'm assigning this chain to okay to be public?
Does the property name ChangeObservable align with what most people would consider best practice when using the .net reactive extensions?
Do I need to call Dispose on any of my subscriptions to this chain, or is it safe enough to leave everything up to garbage collection when the containing class goes out of scope? Keep in mind, I'm observing events from watcher, so there's some shared lifecycle there.
Is it okay to take an observable and wire them into an event on my own class (Changed in the example above), or is the idea to stay out of the native .net event system and leak my IObservable?
Other tips and advice always appreciated! 😀
Is it okay to leak IObservables from a class that creates them? For
example, is the property I'm assigning this chain to okay to be
public?
Yes.
Does the property name ChangeObservable align with what most
people would consider best practice when using the .net reactive
extensions?
Subjective question. Maybe FileChanges? The fact that it's an observable is clear from the type.
Do I need to call Dispose on any of my subscriptions to
this chain, or is it safe enough to leave everything up to garbage
collection when the containing class goes out of scope?
The ChangeObservable.Subscribe at the end could live forever, preventing the object from being garbage collected if the event is subscribed to, though that could also be your intention. Operator subscriptions are generally fine. I can't see the code for your ChangedAsObservable like functions. If they don't include a Subscribe or an event subscription, they're probably fine as well.
Keep in mind,
I'm observing events from watcher, so there's some shared lifecycle
there.
Since FileWatcher implements IDisposable, you should probably use Observable.Using around it so you can combine the lifecycles.
Is it okay to take an observable and wire them into an event on
my own class (Changed in the example above), or is the idea to stay
out of the native .net event system and leak my IObservable?
I would prefer to stay in Rx. The problem with event subscriptions is that they generally live forever. You lose the ability to control subscription lifecycle. They're also feel so much more primitive. But again, that's a bit subjective.

React: Are classes without state still considered stateless/pure?

I've been refactoring my app to make more components stateless/pure components; i.e., they're just functions. However, I noticed that some components will need to connect with the redux store via mapStateToProps. Which causes me to do something like this:
const someComp = (props) => {
const {
funcFromReduxStore,
} = props;
return (
...
<SomeComponent
func={ funcFromReduxStore(myArgs) }
...
);
};
This will not work because I am executing funcFromReduxStore. An easy solution is to wrap the prop in an arrow function. However, this causes many unnecessary re-renders b/c the function won't be bound.
The question then becomes: How do I bind a function in a stateless component?
Is it still stateless if I make it a class, without a constructor, and create a class instance field as so:
class someComp extends React.Component {
const {
funcFromReduxStore,
} = this.props,
wrapper = (x) => funcFromReduxStore(x) // equivalent way to bind w/ ES8+
render() {
...
<SomeCompnent
func={ wrapper(myArgs) }/>
...
}
}
I don't have a constructor, nor state. I want to keep the comopnent stateless, but I also want to bind the function to avoid unncessary re-renders. I also want to continue to keep it stateless b/c React has stated there will be performance benefits for stateless comopnents. Does this qualify as a workaround?
Short answer, no. Stateless functional components need to be simple functions.
You should take a look at the Recompose library for some really cool helpers that allow you to beef up your SFCs.
If you're trying to prevent unnecessary re-renders, you could look into onlyUpdateForKeys() or pure().
EDIT: So, I've been thinking about this a bit more and found this really great article on React component rendering performance. One of the key points in that article that pertains to your question:
Stateless components are internally wrapped in a class without any optimizations currently applied, according to Dan Abramov.
From a tweet in July 2016
So it appears that I was wrong. "Stateless Functional Components" are classes...for now. The confusing thing is that there have been performance improvements theorized:
In the future, we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations.
At this point, I think the answer to your question becomes largely subjective. When you make a class that extends a React Component, any instances of your class get the setStateprototype method. Meaning you have the ability to set state. So does that mean it's stateful even if you're not using state? Thanks to #Jordan for the link to the code. SFCs only get a render method on the prototype when they are wrapped in a class by React.
To your point about wanting to bind functions, there's only two reasons I can think of that you'd want to bind the function:
To give the function access to this (the instance of the component). From your example, it doesn't seem like you need that.
To ensure that the function passed as a prop to a child component always retains the same identity. The wrapper function in your example seems unnecessary. The identity of the function is determined by the parent component (or mapStateToProps, or whatever HOC).
You should also take a look at React's PureComponent which does the same kind of shallow checking that the pure() HOC from recompose does.