Comments (11)
Hi,
I'm not sure whether it fits your current approach, but I think I would
express it a bit along the following lines:
class Point {
constructor(x, y) {
extendReactive(this, {
x: x,
customY: y,
useDoubler: false,
y: function() {
return this.useDoubler ? this.x * 2 : this.customY
}
})
}
}
Does that make sense in your use case?
On Wed, Sep 23, 2015 at 8:32 AM, KS Sreeram [email protected]
wrote:
Let’s say I have a class Point defined like this:
class Point {
constructor(x, y){
extendReactive(this, {
x: x,
y: y
});
}
}Point has two independent observable properties x and y.
For a specific instance of Point I would like to constrain y so that it
is always twice x. I’m currently doing it this way:var p = new Point(10, 20);
sideEffiect(() => {
var newY = p.x * 2;
setTimeout(() => {p.y = newY;}, 0);
});I’m using setTimeout because I can’t update the state inside a
reactive-view. But it’s pretty clunky. Is there a better way to do this? I
know I can use plain JS objects instead of custom classes, but in my actual
scenario I need to use custom classes because they are more complex and
have methods on them.Thanks in advance!
—
Reply to this email directly or view it on GitHub
#31.
from mobx.
It doesn't work from a separation of concerns point of view. The problem is that Point
has no knowledge of the constraint. It's the use-site of Point
that knows about the constraint. Also, different use-sites impose different constraints.
from mobx.
That sounds like a complicated case / model? Maybe use separate subclasses
per constraint? Or if there are many variations, inject the constraint in
the Point class and make all attributes of the Point just view functions
that use the constraint?
Note by the way that current error in the console is at the moment merely a
warning, the code doesn't really throw so at the moment your solution
should work even without the timeout.
I'm still searching what the best strategy is here, I feel that
conceptually views should remain pure so that you can reason about them
more easily, but on the other hand, maybe there are indeed valid use-cases
for not having pure views. So, could you elaborate a bit more on your use
case?
On Wed, Sep 23, 2015 at 9:13 AM, KS Sreeram [email protected]
wrote:
It doesn't work from a separation of concerns point of view. The problem
is that Point has no knowledge of the constraint. It's the use-site of
Point that knows about the constraint. Also, different use-sites impose
different constraints.—
Reply to this email directly or view it on GitHub
#31 (comment)
.
from mobx.
There are multiple scenarios where I’m facing this problem. Here is one scenario:
I have a InputField
component that has two state variables: value
which is a string, and errorMode
a boolean flag. value
is displayed on-screen and is editable. When errorMode
is true, the component is displayed with a light-red background to indicate an error.
As far as the InputField
component is concerned, the two properties are independent. The InputField component is only concerned with display and styling.
If I use this component for entering an email address, I’ll need the following constraint:
errorMode = !isValidEmail(value)
If I use this component for entering a number, then I’ll need this:
errorMode = !isValidNumber(value)
Another case is a sign-up form which contains an InputField
for email. errorMode
can be set if the field contains an invalid email address or if the server has informed us that the email address is already in use. The constraint here is a bit more complex.
Btw, thanks for letting me know the MObservable error is just a warning. I’m planning to disable the warning in my local copy. Do you think this change could cause other problems with MObservable?
Thanks for the help.
from mobx.
There are no technical limitations at the moment that require the warning,
it's merely a conceptual thing. So it is still up for discussion whether it
should be allowed from a functional point of view, not from a technical
point of view. In issue #30 we had the same kind of discussion, and it that
case it fitted nicely in pure views in the end. But if people keep running
into this limitation, maybe I make it a configurable flag. Note that the
principles behind it are explained here:
http://mweststrate.github.io/mobservable/intro/concepts.html
Your errorMode example I would roughly solve as follow:
var errorState = makeReactive({
value: null,
validator: null,
errorMode: function() {
if (!this.validator)
return false;
else
return !this.validator(this.value);
}
}
And then simply:
errorState.validator = isValidNumber;
errorState.value = "hi"
If async communication gets into play, you could also use observe
(previously called side effects) functions and merge the different inputs
in errorMode
:
var errorState = makeReactive({
value: null,
validator: null,
errorMode: function() {
return this.validationError || this.emailIsInUse
},
validationError: function() {
if (!this.validator)
return false;
else
return !this.validator(this.value);
},
emailIsInUse: false
}
observe(function() {
server.validateEmail(errorState.value, function(isInUse) {
// triggered async, so it is allowed to update state again.
errorState.emailIsInUse = isInUse;
});
});
Btw, there is now also an observeAsync
method that basically exposes your
original timeout pattern, see the last comment in issue #28
from mobx.
Maybe you could make pure views the default and have a simple opt out option, something like "use strict" in Javascript. Development team leaders everywhere could enforce best practices by requiring a special dispensation, or maybe a majority team vote, if someone had a special need to use the opt-out directive. I love the chapter from the Tao Te Ching which, loosely translated, says that a bad emperor is feared by the people; a better emperor is loved and admired by the people; but the best emperor is unknown to the people. When the goal is accomplished, the people say, "We did this ourselves." Meetings are generally a waste of time, but a few meetings to devise strategies for making code maintainable, in which a true consensus is reached, could result in a team which says, "We thought of this ourselves", largely oblivious to the leader's deft, unobtrusive manipulation of the process.
For your published library, my preference would be to provide the means in the API to manipulate state during the computation of a reactive view. I stopped using the Yesod Haskell web application framework because it was so 'opinionated'. Everyone is entitled to an opinion, but Michael Snoyman apparently can't bear to know that people are writing sloppy, buggy code with his library, so he imposes his opinions on library users by declining to expose functionality they might want. He explains this well, http://www.yesodweb.com/, and his opinions are first-rate and deserving of the utmost respect, but In order to get things done in 'off the beaten path' use cases, I sometimes needed to alter his library code. I would have preferred to have been given more options in the API.
The Glasgow Haskell Compiler tolerates Haskell coders' use of functions that compiler authors rely on to communicate directly with machines and with the C language. These are not Haskell pure functions, and could easily result in buggy, hard-to-maintain code. The two most-often-used functions in this category are 'unsafeCoerce' and 'unsafePerformIO' and its variations. I generate random numbers for the dice roll in the 'Game of Score' app. No referential transparency there. It uses the notorious 'unsafePerformIO hack'. I don't worry about it because I don't extract the numbers from the IO Monad which contains the numbers until I get to the final function 'main' and broadcast them to 'Score' game players. In one of the first versions, I used 'unsafePerformIO' to extract the numbers right in the middle of the program. The numbers were never used by the program and the compiler never saw them again after they were generated, so all I had to worry about was someone reading the code on Github and forming a low opinion of my coding ability. The point I want to make here is that Haskell programmers and people reviewing their code are reminded of the riskiness of using these functions by their names. Maybe your directive for overriding best practices could be "use questionable coding practices' or 'use dangerous'.
I cleaned up part of my'mobservable-monads' repo last night and added some commentary in the 'Working With Numbers" section. I know you are extremely busy, and I don't want to impose upon your time, but here's a suggestion: I think you would enjoy the "Working With Numbers" section of "mobservable-monads, and possibly find the Haskell monad concept of pulling a value out of one monad and mapping it to an instance of possibly even another type of monad monad either (1) thought provoking, (2) essentially what you are already doing, (3) boring, (4) something else. Commentary for the whole "Working With Numbers" section is only 12 or so sentences. I reproduced the section in README.md at https://github.com/dschalk/mobservable-monads, so you can see what I am talking about at that repo.
And here is a heartfelt recommendation : Even if you are certain that you will never have any use for Haskell, I think you will find (if you haven't already done so) Part II of Simon Marlow's "Parallel and Concurrent Programming in Haskell" to be almost spine-tingling in the poetry of its clear exposition of some of the most simple yet powerful code you will ever see. This is elegance par excellence. The concurrency section, Part II, starts on Chapter 7, "Basic Concurrency: Threads and MVars". In my "Game of Score" project, I use a modified MVar called a 'TMVar, which is explained in Chapter 10, "Software Transactional Memory". It facilitates atomic updates.
The TMVar in "Game of Score" is named ServerState. It contains the current list of connected players, the groups they are in, and their current scores. My app has immutable state, according to the Haskell concept of 'immutable', but players, their groups, and their scores constantly change when players compete; and current state is always available upon request from the ServerState TMVar. The trick is that state is stored in the immutable TMVar. Updates involve removing the state, making a new state that differs from the removed one in the desired way(s), and inserting the new state in the empty TMVar. In a Javascript application, mutating the encapsulated state without removing it would likely be the way to go. I'm still mulling this over and sleeping on it, but it occurs to me that mobservable might benefit from doing something similar; something which would allow more developer flexibility regarding dynamic aspects of state without using view functions to mutate state (in the haskell sense of mutating) during computations. In a Node.js websockets application, such a thing might even be useful in the server for managing state, much as Haskell TMVars handle state in my game app.
I think it is best to err on the side of giving developers too many options in the API, rather than too few. Warnings and flags with alarming names are good, but in the end, give them an API with functionality that allows them to do whatever wild and crazy (but possibly beneficially innovative) things they want.
from mobx.
I made the mobservable-monad's README.md more legible.
from mobx.
The next version introduces a mobservable.strict
setting, which, if set to true
(the default) throws if a view changes a state value, but accepts it if set false
. This way users can apply mobservable in the style that suits there needs and we can investigate later on how both options work out in general.
from mobx.
Perfect! The low-keyed name feels better than 'unsafeCoerce'.
Maybe with mobservable.strict = false and logLevel = 2, you could still send warnings to the console.
from mobx.
Good point! I'll add that.
from mobx.
Mobservable 0.7 has been released, including the aforementioned strict
option.
from mobx.
Related Issues (20)
- Flow JS Types not longer exported in package build? (Upgrade MobX 4 -> 6) HOT 1
- observable is not working when placed inside a function HOT 3
- @action @observable not work in @observer Class Component HOT 4
- Uncaught ReferenceError: process is not defined HOT 1
- mobx-react @inject does not work with new decorators HOT 1
- Using makeAutoObservable before variable initialization doesnt trigger state changes HOT 1
- @action field uses addInitializer from decorator context but that does not exist in the (new) spec HOT 2
- TS complains about @override
- `StubArray` workaround is incompatible with terser's `unsafe_arrows` option HOT 4
- mobx-react-lite component wrapped in `observer` renders twice in latest Next.js HOT 3
- Peer dependency issue for [email protected] HOT 4
- Migration from Mobx 4 > 6 HOT 6
- `trackDerivedFunction` pre-allocates memory for new deps array too aggressively HOT 1
- Invalid/extra warning in observable.box(array)
- Recommended pattern `MyComp = observer(function MyComp() {...})` leads to subtle bug
- Storing a Lit template in a mobx store does not work HOT 3
- Cannot update past `6.10.0` HOT 1
- Supporting of new Set methods in observable Sets HOT 3
- [feature] Investigate running MobX on top of signals standard / polyfill HOT 1
- [eslint-plugin-mobx] ESLint v9 Support
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from mobx.