Why is a Map in JS considered non serializable in redux? - javascript

When using redux and you try to store a map into it, you get an error:
A non-serializable value was detected in the state
Why is that? I know the doc says you can't but it doesn't explain why. Because you can't directly JSON.stringify a map? But you can do something like this...
m = new Map()
m.set("1", "2")
JSON.stringify([...m]);

A map can under specific cirumstances be serializable and de-serializable if you provide the logic by that by hand, using map.entries() and Map.fromEntries(...).
Assuming that all keys and values of the map are themselves serializable - which is not necessarily the case, since maps can take any object as a key (and value, of course).
It cannot be serialized automatically by calling JSON.serialize in a way that would allow you to reconstruct it by calling JSON.parse - and that is what most tools rely on.
So, using a Map could break middleware like redux-persist (unless you add more logic there) and countless others and sometimes even (depending on what you put in here and what you use as keys) even go so far as to crash your DevTools.
Since a map that only contains serializable values & keys has no real benefits over just using a normal object for the same purpose, we recommend just using plain JavaScript objects instead of maps.

Related

Immutable javascript with direct property access

I'm implementing Immutable.js in a set of React components with props containing objects, where Immutable has not been used before.
While I was rewriting all { someProp.someValue } to { someProp.get("someValue") } (since Immutable don't have the props directly accessable), I am just wondering if it could have been possible to have the object properties directly accessable while the object is still immutable.
I guess the reason is that if they where directly accessable, they would be mutable because that's how javascript objects work. However, could it not be possible to freeze the objects (in browsers supporting it of course), and still have the mutating methods (.set, .map etc) create copies instead of changing the object itself the way Immutable work?
Is that technically possible, and is there any library doing something like this allready?
"Is that technically possible"
Yes, the implementation you described would solve that particular problem, but Immutable does more than just provide data structures you're not allowed to change directly:
Immutable provides Persistent Immutable List, Stack, Map, OrderedMap, Set, OrderedSet and Record. They are highly efficient on modern JavaScript VMs by using structural sharing via hash maps tries and vector tries as popularized by Clojure and Scala, minimizing the need to copy or cache data.
Immutable also provides a lazy Seq, allowing efficient chaining of collection methods like map and filter without creating intermediate representations. Create some Seq with Range and Repeat.
Emphasis mine.
So, if you have a huge set of nested immutable Maps and Lists, and you change one element (that is: you create a new structure with mostly the same data but one item different), Immutable will be able to share a lot of the data between the two data structures, making the operation faster and consuming less memory.
However, this means that the someValue value isn't necessarily stored at the top of the data structure, waiting for you to access it; Immutable has to reach into internals to find it for you, thus the need for get.
I suspect you could create a very cool and friendly API using Symbols (to hide the underlying data structures) and Proxies (to intercept proprety accessors), but browser support is practically non-existent.
"and is there any library doing something like this already?"
The closest thing I can think of at the moment is React's immutable helpers. The update syntax is a bit noisy in order to support a wide range of operations, but what you get out the other end is are plain JavaScript objects and arrays.
Dealing with immutable data in JavaScript is more difficult than in languages designed for it, like Clojure. However, we've provided a simple immutability helper, update(), that makes dealing with this type of data much easier, without fundamentally changing how your data is represented.
The helper sort of assumes the objects you're passing in are implicitly immutable, but there's no reason you couldn't deeply freeze them as well.

Find Leaflet map objects present on page, without a variable reference

I have an idea for a browser plugin which would manipulate Leaflet maps on third-party sites. But I'm stuck on a simple thing: I'm not sure how to discover Leaflet map DOM elements on a page, and then access the associated Leaflet map object.
Is $('.leaflet-container') a reliable way to find all map objects?
How to actually access the map object from that object, so I can do something like: $('.leaflet-container').eachLayer(...), which doesn't work.
This question is essentially the same as How can I get a leaflet.js instance using only a DOM object?, but the answer there was a workaround which doesn't help.
Yes, that should be sufficient, although it's not a documented behavior and could at least theoretically change in future versions of Leaflet
There's no way to do this. The reference to the map is owned by the code creating the map, and it could have discarded it or might be storing it in a location you do not have access to. Leaflet itself does not store a reference to the map anywhere where you can access it
On a side note, it is my opinion that you should rather let users of your code explicitly pass references to you, rather than you trying to automatically find references. See for example the inversion of control principle, where the caller supplies dependencies; Law of Demeter is also somewhat applicable - don't pry into other codes internals (unless you really, really have to).
OK, so this is a solution that could work, but it is brittle and limited. If you have a way to more directly find the reference, that's ideal.
I wanted to get a map reference where I do not want to modify upstream code; it's a one-off where brittleness is OK.
I know my map is going to be defined on the window.FOO scope (i.e. it's a global reference) and that it will be in the format map0000 where 0000 is a random number. So I constructed a quick function to scan the global window object's properties for variables matching this pattern.
window[Object.keys(window).find(key => key.substr(0,3) === "map")];
This returns a Leaflet map reference but could break if there is more than one map on the page. You could also add a validation that it's a real Leaflet map by testing it's properties.
Again, this is not ideal, but, if your use case is limited enough, it is one way to achieve this. Thanks!

What is the Ember way of converting retrieved Ember Data records into plain objects?

I have retrieved a series of records using var items = store.find('model');. The returned object is an instance of RecordArray, and contains several entries, each with an Ember object that allows me to get and set properties into the records.
It all looks pretty good.
Now I need to feed the returned objects into a third party library, and of course I cannot send Ember objects there since it expects plain objects.
I looked on pages and pages of related material but I can't find any generic way of doing this. I'm pretty sure there is one since this seems to be a very basic use case, so I don't
want to reinvent the wheel and write it all again.
Is there a facility in Ember for that? How can I obtain a simple array with plain JavaScript objects (just hashes, I mean) from this RecordArray I got?
UPDATE
Of course I can do JSON.parse(JSON.stringify(recordArray)); but for large objects that doesn't seem too performant with so many conversions. I'm wondering if Ember provides a more direct way (with better performance) of doing this.
Thanks!
As far as I know there is no ObjectSerializer so probably easiest way is to use JSONSerializer and use JSON.parse to create objects out of them.
items.map(function(e){
return JSON.parse(e.toJSON());
});
However, you can manually write serialization logic.
function serializeToObject(model){
var fields = Ember.get(model.constructor, 'fields');
obj = {};
fields.forEach(function(fieldName, kindOfField){
obj[fieldName] = model.get(fieldName);
});
return obj;
}

JS: Serializing an object with methods (or an instance) to json/string without losing the methods

Bumped into this while trying to cache objects into localstorage. seems that localstorage currently doesn't support objects with methods/object Instances etc. just POJO that are serialized into a string. In python there's pickle that can handle this (or marshal before it) but I couldn't find a js equivalent. Since I guess I'm not the first Js dev to need this feature I guess there are already proven solutions (either in patterns or external libs) to this problem which I'm missing.
Basically I'm looking for a way to call serialize(object) into a json/string and later deSerialize(objString) that would return the original object/instance etc.
The answer can be some lib and doesn't have to be part of the js standard library.
I use JSONfn do that. It has its own stringify/parse methods that save functions as well as ordinary JSON data.

localStorage - use getItem/setItem functions or access object directly?

Are there some benefits of using the methods defined on the localStorage object versus accessing the object properties directly? For example, instead of:
var x = localStorage.getItem(key);
localStorage.setItem(key, data);
I have been doing this:
var x = localStorage[key];
localStorage[key] = data;
Is there anything wrong with this?
Not really, they are, basically, exactly the same. One uses encapsulation (getter/setter) to better protect the data and for simple usage. You're supposed to use this style (for security).
The other allows for better usage when names(keys) are unknown and for arrays and loops. Use .key() and .length to iterate through your storage items without knowing their actual key names.
I found this to be a great resource : http://diveintohtml5.info/storage.html
This question might provide more insight as well to some: HTML5 localStorage key order
Addendum:
Clearly there has been some confusion about encapsulation. Check out this quick Wikipedia. But seriously, I would hope users of this site know how to google.
Moving on, encapsulation is the idea that you are making little in and out portals for communication with another system. Say you are making an API package for others to use. Say you have an array of information in that API system that gets updated by user input. You could make users of your API directly put that information in the array... using the array[key] method. OR you could use encapsulation. Take the code that adds it to the array and wrap it in a function (say, a setArray() or setWhateverMakesSense() function) that the user of your API calls to add this type of information. Then, in this set function you can check the data for issues, you can add it to the array in the correct way, in case you need it pushed or shifted onto the array in a certain way...etc. you control how the input from the user gets into the actual program. So, by itself it does not add security, but allows for security to be written by you, the author of the API. This also allows for better versioning/updating as users of your API will not have to rewrite code if you decide to make internal changes. But this is inherent to good OOP anyhow. Basically, in Javascript, any function you write is a part of your API. People are often the author of an API and it's sole user. In this case, the question of whether or not to use the encapsulation functions is moot. Just do what you like best. Because only you will be using it.
(Therefore, in response to Natix's comment below...)
In the case here of JavaScript and the localStorage object, they have already written this API, they are the author, and we are its users. If the JavaScript authors decide to change how localStorage works, then it will be much less likely for you to have to rewrite your code if you used the encapsulation methods. But we all know its highly unlikely that this level of change will ever happen, at least not any time soon. And since the authors didn't have any inherent different safety checks to make here, then, currently, both these ways of using localStorage are essentially the same. Except when you try to get data that doesn't exist. The encapsulated getItem function will return null (instead of undefined). That is one reason that encapsulation is suggested to be used; for more predictable/uniform/safer/easier code. And using null also matches other languages. They don't like us using undefined, in general. Not that it actually matters anyhow, assuming your code is good it's all essentially the same. People tend to ignore many of the "suggestions" in JavaScript, lol! Anyhow, encapsulation (in JavaScript) is basically just a shim. However, if we want to do our own custom security/safety checks then we can easily either: write a second encapsulation around the localStorage encapsulate, or just overwrite/replace the existing encapsulation (shim) itself around localStorage. Because JavaScript is just that awesome.
PT
I think they are exactly the same, the only thing the documenation states is:
Note: Although the values can be set and read using the standard
JavaScript property access method, using the getItem and setItem
methods is recommended.
If using the full shim, however, it states that:
The use of methods localStorage.yourKey = yourValue; and delete
localStorage.yourKey; to set or delete a key is not a secure way with
this code.
and the limited shim:
The use of method localStorage.yourKey in order to get, set or delete
a key is not permitted with this code.
One of the biggest benefits I see is that I don't have to check if a value is undefined or not before I JSON.parse() it, since getItem() returns NULL as opposed to undefined.
As long as you don't use the "dot notation" like window.localStorage.key you are probably OK, as it is not available in Windows Phone 7. I haven't tested with brackets (your second example). Personally I always use the set and get functions (your first example).
Well, there is actually a difference, when there is no local storage available for an item:
localStorage.item returns undefined
localStorage.getItem('item') returns null
One popular use case may be when using JSON.parse() of the return value: the parsing fails for undefined, while it works for null

Categories

Resources