-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Fixes #509: pass callback on setState and setProps #617
Conversation
if (this.root !== this) { | ||
throw new Error('ReactWrapper::setState() can only be called on the root'); | ||
} | ||
this.instance().setState(state); | ||
this.instance().setState(state, cb); |
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.
does react handle throwing if cb
isn't a 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.
Yea seems like it's taken care of here:
https://github.com/facebook/react/blob/master/src/renderers/shared/stack/reconciler/ReactUpdateQueue.js#L257
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.
Awesome - it would still be good to add tests for that behavior, in case a future version of react changes 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.
Yea sounds good. I'll add a test.
* @returns {ReactWrapper} | ||
*/ | ||
setState(state) { | ||
setState(state, cb) { |
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.
since this param is optional, it should default to undefined
so it doesn't count towards the function's length (same throughout)
expect(wrapper.find('.foo').length).to.equal(1); | ||
wrapper.setProps({ id: 'bar', foo: 'bla' }, () => { | ||
expect(wrapper.find('.bar').length).to.equal(1); | ||
}); |
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 there a way we could add a sync assertion that the length is NOT equal to 1, to prove that the callback is doing its job?
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.
It is difficult to force setState
to act asynchronously. In most cases it does act synchronously it's just not a guarantee.
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.
hm - the motivation for adding this callback is those cases where it does act async. there must be some likely usage pattern that forces 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.
I'm not sure usage patterns that setState does act async in testing. but I think you can use batchedUpdates
like this.
import { batchedUpdates } from '../src/react-compat';
batchedUpdates(() => {
wrapper.setProps({ id: 'bar', foo: 'bla' }, () => {
expect(wrapper.find('.bar').length).to.equal(1);
});
expect(wrapper.find('.bar').length).to.equal(0);
});
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.
thanks @koba04. Using batchedUpdates
worked.
@@ -32,9 +32,9 @@ export default function createWrapperComponent(node, options = {}) { | |||
}; | |||
}, | |||
|
|||
setChildProps(newProps) { | |||
setChildProps(newProps, cb = undefined) { |
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.
Wont cb
be undefined
by default if its not passed in?
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, but defaulting it explicitly makes it not count towards the function's length.
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.
What are the advantages of doing that here? Specifically for setState
which is meant to shadow the React API, which does not make this explicit.
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.
Function lengths are intended to only count required arguments - the language follows this for the most part. You're right that React doesn't make this explicit, but I'd prefer to correct their mistake for our API.
4ed9c95
to
7b94db8
Compare
Awesome work! Well, should it be shipped? |
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 LGTM - it would still be preferred, imo, to have a test that guarantees that the setState
callback is async (we don't have that right now) - but I don't think that needs to block.
@ljharb I added a test where the callback is called asynchronously ( 7b94db8#diff-2f849fa739461853c70ddd193891074fR976 ) |
@alecrobins awesome, thanks! Would you mind rebasing so as to remove all merge commits, and then this is good to go. |
@alecrobins can you rebase this again for us when you have a moment? Thanks! |
@@ -57,7 +57,7 @@ | |||
* [render()](/docs/api/ShallowWrapper/render.md) | |||
* [setContext(context)](/docs/api/ShallowWrapper/setContext.md) | |||
* [setProps(nextProps)](/docs/api/ShallowWrapper/setProps.md) | |||
* [setState(nextState)](/docs/api/ShallowWrapper/setState.md) | |||
* [setState(nextState[, cb])](/docs/api/ShallowWrapper/setState.md) |
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.
It would be nice to use callback
for this variable (here and elsewhere), for maximum clarity.
@lencioni Just rebased and changed the variable name to |
LGTM |
hi! when will this get released? Looking forward to do this in tests: flushSetState = (wrapper) => new Promise(resolve => wrapper.setState({}, resolve));
...
await flushSetState(wrapper);
expect(wrapper.state(...)) |
@kentor This has been released in |
Resolves #509
Due to the asynchronous nature of setState, we can't assume a
wrapper.setState
orwrapper.setProps
(which usessetState
in thesetChildProps
call) will act synchronously.This PR adds callback functions to the
stateState
andsetProps
functions for full DOM rendering andsetState
for shallow rendering.Example: