-
Notifications
You must be signed in to change notification settings - Fork 182
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
Serialization hooks #126
Serialization hooks #126
Conversation
…o Store and wrapStore
This reverts commit a3fd0c1.
@ethanroday yeah by default our store functions aren't bound to the instance (we can get around this in the future by using Class properties). I will get to reviewing this PR in a bit today. Thank you so much for this beast. The documentation looks stellar, and I think this is a sane entrypoint into giving people flexibility without sacrificing the default, out-of-the-box redux behavior. |
@vhmth got it. Glad you think it makes sense! It’s a little suboptimal in the sense that it exposes some internals to the user (patch message payloads are more than just slices of the store’s state), but I imagine in most cases that will be okay. |
Hey @vhmth, have you had a chance to look this over? |
@ethanroday Have this on my todos. Trying to get to this today! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vhmth This looks good to me after going through it. Would love your thoughts as well.
The only change that is required is to fix whatever is breaking the build after the merge from master. The master merge just contained a simple addition from #134 to make the extension id null
instead of an empty string.
{ | ||
id: 1, | ||
text: 'Write a Chrome extension', | ||
created: {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this true? I swore it turned it into a string last time I checked... am I crazy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Vanilla JSON.parse(JSON.stringify(new Date()))
will give you a string, but Chrome's default serialization/deserialization for messaging gives you an empty object for some reason or another.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it - must have been confusing the two. Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow this is really good to know. I could see us having a serializers
dir with defaults for things like dates (not necessary for this PR ofc).
|
||
wrapStore(store, { | ||
portName: 'MY_APP', | ||
serializer: payload => JSON.stringify(payload, dateReplacer), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that these were added as options rather than additional parameters to wrap store 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the names too (dateReplacer
and dateReviver
). Makes it clear what's going on
@@ -0,0 +1,91 @@ | |||
export const noop = (payload) => payload; | |||
|
|||
const transformPayload = (message, transformer = noop) => ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be good to mention here why a new object is required rather than mutating the message. It took me a second to realize that manipulations here would manipulate the original action object which could cause unintentional side-effects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will add a comment.
The |
@vhmth would love a second pass from you! This is a pretty big change! |
@@ -9,6 +9,8 @@ export class Store<S = any, A extends redux.Action = redux.Action> { | |||
portName: string, | |||
state?: any, | |||
extensionId?: string, | |||
serializer?: Function, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you for adding the TS defs
// just return a copy of the message. | ||
// We return a copy rather than the original message so that we're not | ||
// mutating the original action object. | ||
...(message.payload ? {payload: transformer(message.payload)} : {}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice - love the cascading spread
@@ -255,6 +298,21 @@ describe('Store', function () { | |||
}).should.eql(true); | |||
}); | |||
|
|||
it('should serialize payloads before sending', function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests are incredible. Mind adding one more test that serializes/deserializes a date object and checks the input's (date originally stored in action) getDate
to the output's (date received after deserialization)? Another check in this test that would be good is checking that the two date objects are not triple-equal to each other to confirm that serialization/deserialization indeed happened.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missed the boat on this one, but hopefully the existing tests are sufficient. If it helps, I've been using this in production for a couple of months and all seems well.
@@ -294,4 +346,24 @@ describe('Store', function () { | |||
return p.should.be.fulfilledWith(undefined); | |||
}); | |||
}); | |||
|
|||
describe("when validating options", function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very very nice!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than my comment about adding another test, everything else makes so much sense. This PR is awesome!
Great! Let's finalize this thing! |
Absolutely down. My vote would be to actually increase by a major version but seems like I missed the boat on that one! Ah well - cool with |
Thanks for the shoutout, @vhmth! Happy to be contributing to something that's been so useful to me 😄 |
@vhmth We can always bump it to 2.0 alpha! Why would you vote major, just curious? @ethanroday Yes, thank you very much for your fantastic contribution. I echo @vhmth 's sentiment completely. Regarding the additional test, you can always add it as a separate PR :) |
@tshaddix primary reason for the bump to major is I look at "plug and play" layers as a step towards increasing complexity in many ways. In this case we've introduced a custom serialization that diverges away from the standard Chrome behavior. I smell lots of issues potentially coming up since this interferes with the core communication mechanism b/w background and content processes. |
@vhmth Ah, I see your point and I see my point 😄 I'm happy to bump major when on next publish. |
@tshaddix that works! |
serializer
anddeserializer
options forwrapStore
andStore
, which will serialize all outgoing messages and deserialize all incoming messages.Note: When testing in an actual extension, I found it necessary to wrap all of the messaging functions (e.g.
port.postMessage
orchrome.runtime.onMessage.addListener
) in anonymous functions, rather than passing the function references directly. If I didn't, Chrome gave me the following error:I'm not sure what's causing this, but would welcome ideas. It feels unnecessary to have to wrap all of the calls.