-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dealing with the MobX array != real array footgun #806
Comments
Hi @spion, Yes this is one of the most awkward things by far in MobX indeed. Some random thoughts
|
See also #595 |
@mweststrate regarding the first suggestion, monkey patching would only be done to throw an error or emit a warning. This would only serve as a reminder to developers not to use observable arrays in that way. When you get to non-monkey-patched production with hopefully no warnings/errors, the behaviour would be exactly the same |
I think this is a cool idea, however need to first add an option to have a distinction between dev / production code like react. That mechanism could also be used to solve #653, and optimizing by skipping many invariant checks. Will open a separate issue for that |
I agree with @mweststrate point 4: I never use "raw" observable values outside of the state, but only computed values. This has also an added benefit, because it's a good practice to auto-contain in the state the implementation of the transformation of a value. This avoids duplication and repeated bugged behavior. @spion do you think that could help on the matter? Could you post a real-case scenario in which you concat? |
@caesarsol It is a computed value e.g.
Due to the the initial value being |
@spion Ah, you are right then, that's a surprising edge case. I probably didn't encounter it before because I was lucky... I think the best way to get others to avoid it is to give a good rule to follow. For example, I think the real issue here is the acc.concat(...store.items.map(i => i.name)) However, your problem might be better solved by using _.uniq(_.flatMap(subStores, store => store.items.map(i => i.name))) |
@caesarsol The lodash solution will also break, since _.flatMap also uses Array.isArray under the cover. The footgun is scarier than one would think! 🙃 My preferred solution would be for TypeScript to support decorators that modify the type of the property... In absence of that, concat and isArray can be monkey-patched. |
@spion Sorry, you are right... I was pretty sure it would have worked, since mobx arrays are iterable. 😿 I've tried the solution with the spread operator |
Actually reading some more code, it seems lodash @mweststrate is it possible? I'm not sure about support, being it ES6, and by reading in the official polyfill Do you think adding it is a risk, because would change behavior depending on the ES6 support? |
@caesarsol if we put in isConcatSpreadable, it still wont work in many browsers because they don't support the symbol, so we get inconsistent behaviour, which is arguably worse 😞 |
I just had an idea that might work. If we define a getter for isConcatSpreadable that immediately throws an error, we would at least get that warning in modern browsers (and modern copies of lodash). Best part is that it works without monkey patching. I'm going to try this out. |
What about the Adding custom methods doesn't seem right either, especially if they might conflict with existing specs. Mentioning it in an obscure paragraph of the documentation doesn't help much either. It's just a recipe for disaster and you can easily shoot yourself in the foot even if you are aware of the issue. |
@use-strict Well, it's always a class different from an Array! @spion Any success with your experiment? |
@spion the get idea is neat, did it work? |
Closing issue as #1076 will address this |
Another solution is to redefine "concat" method of native Array class to throw error when Mobx collection is passed. |
Would love to see some CLEAR examples of populating observable arrays with async calls then using the data via the store. I am having huge pains in my native app with mob-x not behaving, but there is almost zero clear example of what I should actually be doing. Presume I gave a store with an observble array: @observable myArray = [] then in my The in my render function I reference the array to form a list
However, its throws an error the list remains still empty, even console logging and doing an empty check yields absolutely no results. Should I some how be iterating the data and pushing it into the array entry by entry? or what does replace actually achieve here.? Would really love some insight, the documentation is massively lacking. |
Please, refrain from commenting on old and closed issues as only a few people will see that. Open a new issue with a link to this one instead. |
I know its already listed on the pitfalls page, but I think this is a serious enough issue that it needs more effort.
Its really easy to accidentally use a MobX array in a context where it wont do what you expect. The most common one of those is the concat operation: even with TypeScript on board its easy to concat a MobX array to a regular array and get a nested array.
The second most common source of issues are lodash methods. Not sure why they fail - perhaps because they use
Array.isArray
?Depending on the code, the issue might propagate quite far before it manifests as an actual bug - mostly due to JavaScripts wonderful array of implicit conversions. Arrays of strings (and arrays-of-arrays-of-strings) are especially vulnerable, since element[k] returns a string in both cases, equality comparison does a lot of coercion, and there are quite a few shared methods (concat, slice, etc).
I run into this issue approximately once per 250 LOC of MobX models. Every time its very hard to track down due to propagation. I'm fairly certain that once other developers join in, this is going to be a recurring nightmare.
I don't have a definite idea on how to fix this (that is, without proxies). Here are the first two ideas that come to mind:
Have a development-mode add-on that will monkey-patch common array methods to warn/throw on common errors. For example, I think it would be safe to throw whenever you try to concat a MobX array onto a regular array, or warn whenever Array.isArray is used on a MobX array.
Faking it, by creating an actual arrays and then assigning
__proto__
to something else. Sounds like something that has been tried already?The text was updated successfully, but these errors were encountered: