-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Support for immutable export/import #278
Comments
You should specify how to deserialize it. See docs and the example for |
How do I access this for the chrome store extension? |
When you add the extension's enhancer, you can specify additional options like in that example from the docs: const store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
deserializeState: (state) => ({
todos: {
...state.todos,
todoList: Immutable.fromJS(state.todos.todoList)
}
})
})); |
Ah great - thanks! |
Hi, this is actually still an issue with the export/import. Here is a snip-it to try on the todomvc example that change every number to foo or bar:
In the UI this does work as expected, but when I download it looks like:
Instead of:
Thanks for looking into this. |
You should use deserializeState / deserializeAction for your case, as you want to change the data that comes back to the client side.
|
Why is
Then when I deseralize the state, I can convert it back into immutable dynamically too. |
We force it to But I see what you want to accomplish. Will include the custom function there if specified. |
Thanks! |
Implemented in A PR to update docs on how to better work with Immutable would be much appreciated. |
Just tested and it does work. I was not expecting it all to be jsan encoded still. Not sure if that is the right thing to do or not with a custom seralizalizer. It does means I'll need to write some translators to be able to use tools like jq to search it |
It also does not seem to honor the deseralizer during import. The export matches expectations (although jsan). I have found a few variants of it failing and I think it has to do with the deseralize not being called. I can also get something related to 'Unexpected key "__isImmutable" found in previous state received by the reducer', but am unable to reproduce this in a small example. Here is a starter example that I think should work. I am testing via just the todomvc example. I make a change, commit it. Make a change then download.
I am also slightly confused about the lack of symmetry between serialize and deseralize so I might be doing something wrong. It would be good if I could provide both in the |
@mlucool, thanks for investigating it. Yes, I've broken it in dcd346e. I'll look into it. Regarding of the lack of the symmetry, the serialize function is passed as the |
Related to #278 and dcd346e79e 6fb1571f53d64f2d6d462128c205d7
Thanks for reopening. Although not ideal, you could detect the number of arguments (or use safer yet use some flag) to make it symmetric and backwards compatible. You could also consider making a breaking change and being declarative about it. I'd prefer the way it is done for deserialize as you feel like you get more control by getting in a whole object and then doing whatever you want to return a serialized version. |
I've fixed the issue with no calling deseralizer during the import in a5e82ab. Before publishing a patch, I'd like to clarify the following:
JSAN takes care of unserializable data (otherwise JSON.stringify will just throw). Immutable data contains functions, which would be "encoded" as well, but since you transform them through your custom
That's because it was added to the root reducer, thus the error from Redux. If the the root reducer is meant to be immutable, then that was caused by the fact that deserializer wasn't called.
As per the previous message, we could add an additional parameter for deseralize function, to pass it as a reviver, and to have the same |
The reason for |
If I'm not missing anything, detecting the number of arguments isn't trivial, we have to stringify the function. We could just pass to |
For length you should be able to do this. I'll be very happy with whatever way you think is better as long as it is symmetric; I see reasons for both. The configuration object sounds ok too! As for JSAN, I understand why it is a great default. The problem is its JSAN inside of JSAN (please correct me if I am wrong). So I need to parse the JSAN (which requires a JS parser) then parse each field back too. This work is very straightforward, but in my small universe we only use plain old data and having things in pure JSON structure means other tools work out of the box. To be safe, we could use JSON with something like this or this. It would be better if we could at least have the option to opt out, if you don't think it will complicate things too much. |
@mlucool, thanks for the details. Then we could use
I'll come with an implementation of As for JSAN, the object is passed to |
Nice search! I guess my point is this replacer/reviver have nothing to do with remotedev and I think there is are great usecases outside of remote devtools (great work here btw!). |
Well, it took more than I expected, but the result is amazing. In addition to importing and persisting immutable data, we'll be able to investigate the datatypes on the monitor: Here's the data and how it gets stringified. The implementation is pretty simple. I guess we could afford 50 lines of code to include into the extension to make the life easier. So will allow just to pass the Immutable library like so: import Immutable from 'immutable';
const store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
serialize: {
immutable: Immutable
}
})); The lib can be used apart from the extension to stringify / parse the immutable data (see the tests). As per The only data type, which is not implemented yet, is import Immutable from 'immutable';
const store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
serialize: {
immutable: Immutable,
refs: { myRecord: ABRecord }
}
})); So, we'll be able to parse back var ABRecord = Immutable.Record({a:1, b:2, c:3})
var obj = { myRecord: ABRecord({a:1, b:2}) } |
This seems great. I don't see where I would inject As for I personally never use Records so I don't have a lot of insight there. Thanks again for all the work on this 👍 |
Yes, that's not there yet. Still was thinking whether we should import it as a library and add to OK, will use Now searching if Since we can reuse this functionality for ES6 Map/Set and TypedArrays, and maybe other custom types (for example, like dealing with |
I have published You can use it like so: import Immutable from 'immutable';
const store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
serialize: {
immutable: Immutable,
// refs: [ABRecord] - for Records
}
})); It also allows to persist the state. Let me know if you have any issues with that. |
The formatting looks great, but I do run into issues with using the extension now. I love the fact that I can now just paste output into a JSON formatter and explore because of the lack of JSAN. I have noticed the following issues:
If you have not seen these, I can try and do a reboot sometime in the next 24 hours. |
I'm confused about |
I tried also the version from Chrome Store and cannot see that happening. If you have that problem with the extension's enhancer, check If it's about |
I was referring to: 'Open in Remote DevTools (Alt+Shift+Up Arrow)' Thanks for the tip on where to find the error. I am seeing this there:
|
That error says that |
Seems like our introduced As you can see from the code above, it could happen when If you cannot provide a repro or at least an example state to reproduce the issue, maybe you could add a test to reproduce that here? |
I'll try and reproduce that test or provide something you can work from |
I am struggling to reproduce this in any meaningful way outside my app. Where would I expect to see this line. The normal console? I want to see what data it rejects. |
@mlucool, that script is running in the extension's background page. You could listen for events (messages) sending from the extension, by adding a listener to you page: window.addEventListener('message', (event) => { if (event.data) console.log(event.data) } , false); There will be some keys of the immutable objects, which you can try to parse with our reviver. I suspect there should be somewhere |
Humm so I have run this and the most interesting/unexpected finding thus far is something like in the computed state string:
Also this:
appMetaData I expect to be an empty map (possibly null?). |
In the first example it's just a reference to In the second example, it's how jsan encodes the I'd suggest to exclude all reducers one by one and see which one fails. Then put there some random data you can share, so I could investigate. |
Thanks for the tip. I have gotten it down to a bug in my code where if I change one thing I can fix/reproduce the bug. It is related to appMetaData (maybe by chance). appMetaData had a bug where it's init state was a plain JS object (but future states which I don't think it ever got to are Immutable). It sat within a higher order reducer whose data was immutable. When I changed the init state to Map() this plugin does work and the export looks like:
This seems to imply that there is an issue when a regular object is within an immutable map. I have tried to create a test for this, but my simple test of parsing a Map with an object in it succeeded when I thought it would fail.
Please let me know if this was not clear or if there is anything else you suggest I try to help you be able to reproduce it. |
Thanks for the details and the investigation! Having plain objects inside immutable maps shouldn't be an issue. It would be much appreciated if could find some time to share an example (a repo or jsfiddle), I can run and reproduce that issue. |
Yeah I can't quite figure out why yet. I am still working to make something that I can share, was updating with the hope it would be obvious to you |
Got it:
At first it works, but add one item to the list and you will see it does add in the UI (i.e. redux works), but the extension stops showing new updates. Errors:
|
@mlucool, thanks! You were right, the problem is when having references like "appMetaData":{
"$jsan":"$[0].state.app.data.appMetaData"
}, Our reviver moves A solution would not to use the reviver here, and traverse the object after it got parsed. But it would be less performant and we'll have an infinite recursion in case of circular references. I'll think into it. |
Any further thoughts? |
@mlucool, do you still experience that issue? I've posted |
Just played around with it some more. As far as I can tell this looks great! I'll continue to use it over the coming weeks and let you know if I run into any more issues. Thanks again for all the work here! |
Although immutable.js data shows up perfectly in this tool, the export of it does not distinguish it from a plain old JSON object. This means that when I import it back, the 'preloadedState' sets the redux store incorrectly.
The text was updated successfully, but these errors were encountered: