-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
New API for connectDecorator implementation #16
Conversation
…rge and puts sequential results of such on props of decorated component. Connector is intended to be deprecated. older connectDecorator is now called connecctDeprecated
This is looking good so far! Can you please remove the old “deprecated” code for cleanness? I don't want to ship the old versions anyway. |
static displayName = `Connector(${getDisplayName(DecoratedComponent)})`; | ||
const emptyBinder = () => ({}); | ||
|
||
const identityMerge = (slice, actionsCreators, props) => ({...slice, ...actionsCreators, ...props}); |
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.
We ran into some annoying bugs with a merge like this, specifically the fact that props can override slice. Given that Connect's primary responsibility is getting state from the store and passing it along, I'm not sure if allowing props to override it by default is right.
Some other options:
- Put props first, allowing slice and actionCreators to override it.
- Do not merge props in by default, a custom merge can be passed in to do this explicitly if desired.
For what it's worth, the specific problem we ran into involved nested connectors and the first connector splatting its props to the 2nd. They both had a loaded
flag, but they meant different things. Nested connectors can happen especially with nested routes.
Also, it looks like props are splatted in again, below, in the actual render
, but in that case they have lower precedence, which is good, I think.
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 agree props
should go first.
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.
Also, if props
go first here, they need to be removed from render
.
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'm fine either way we will just need the eventual documentation to indicate that if you set a custom merge you will need to spread props for them to ever reach your underlying component.
either way this gives more control so that is good
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.
Yes, I think that would be fine.
… component. provide wrapper method to get underlying ref to provide access to unerlying refs methods
componentDidMount() { | ||
if (subscribing) { | ||
this.subscribed = true; | ||
this.unsubscribe = this.context.store.subscribe(::this.handleChange); |
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.
Are you planning to stick to ES6 on this repo too?
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 think it's no big deal here because it's not a public facing example.
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.
Not sure I understand the original comment? I believe the package builds to es5 for consumption by default. Are you referring to not using es7 features or something?
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.
We're removing ES7 features from Redux examples just so people can work with something stable and don't assume Redux requires ES7. But using it in source is fine.
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.
oh gotcha, object spread and all that.
…ctionCreators, remove from DecoratedComponent render
let div = TestUtils.findRenderedDOMComponentWithTag(tree, 'div'); | ||
expect(div.props.children).toBe(42); | ||
|
||
tree.setState({ select: selectB }); |
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.
This test is failing. might just be a jsdom issue but I'm really unfamiliar with react unit testing so I'd love some eyes on this to either fix the test or confirm that this functionality is actually broken in my implementation
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 don't think this test makes sense anymore. select
isn't a prop now, you must specify it during the class definition.
I'm not sure why it breaks but you can remove it.
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.
Alright, i'll remove it
const { Component, PropTypes } = React; | ||
const storeShape = createStoreShape(PropTypes); | ||
|
||
return function connect(select, dispatchBinder = emptyBinder, mergeHandler = identityMerge) { |
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.
should we rename select to selector as mentioned in #15 ?
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'm in favor of arguments being named consistently as nouns or verbs. so select -> selector or change
selector <-> select
dispatchBinder <-> dispatchBind
mergeHandler <-> merge
Cool xD When do you think this will be merged? Can't wait to use it hehe |
About a week after Redux 1.0 is released, I presume. |
ok, thank you @gaearon :) |
exposing underlying component ref for access to component methods via DecoratedComponent#getUnderlyingRef
Should we consider changing the static member DecoratedComponent on our wrapped component. If you use Perhaps we change or |
…ispatch and updated corresponding method names. this is to avoid confusion with the redux utility bindActionCreators and the fact that the second argument doesn not have to necessarily be related to action creators in any way
@Keats Hey Keats, great job! I have a question: If we use the merge method to select inside a store, does it update only when that particular "reselect" gives up different values or does it re-render every time the main store changes? |
+1 just tried it out and it is working as described |
note: nesting connectors does not work in react 0.13, but does work in 0.14. Something to do with the change to parent contexts. |
@Keats oh :P my mistake |
Alright,
Here's my take on the new connectDecorator API. Normal Args examples should all work from @gaearon 's comment in #1
#1 (comment)
API
I didn't delete createConnector but I did move createConnectDecorator to createConnectDecoratorDeprecated until we can decide if we should just delete these.the old Connector and connect decorator have been deleted
I have tested through mocha only so no guarantees it is 100%
I'm happy to make changes/work on it etc... as opinions change on best api going forward if it's not ready to merge yet.
State is handled like Connector did on any notification from the store. shouldComponentUpdate will bail if the slice is the same
actions are bound using the provided dispatchBinder (2nd arg) only on component mount. Not sure if this is what you were aiming for in the API but it certainly reduces the number of binds to a lot less than every render and it seemed find since the typical use case for dispatchBinder is to simply bind dispatch to the action creator methods
merge is called on every render, reading stateProps and dispatchBinderProps from component state (not store state) and taking whatever current component props as the 3rd argument.
finally the decorated component is rendered with this.props, and mergedProps in that order.If you want choose to provide a custom merge function then only the props returned from merge will be added to the underlying component
I also wrote a util for wrapActionCreators that fits the dispatchBinder argument signature and delegates to redux/bindActionCreators. This is called form connectDecorator if the actionCreators argument is a plain object