-
Notifications
You must be signed in to change notification settings - Fork 67
CPLAT-8034 Implement/Expose useState Hook #223
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
Conversation
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.
Just a few things :) excited for this, nice work
@@ -0,0 +1,97 @@ | |||
@JS() | |||
library hooks; |
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 reason we are making hooks a standalone public entrypoint? Any reason to not move this to lib/src/hooks.dart
and then export that from lib/react.dart
like we are doing for the context APIs?
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.
After going back and forth on it, I suggested this offline to makes things a little more organized as opposed to just lumping more things into react.dart
. Also, since they're APIs that will only be used sometimes, I figured it made sense for them to be their own self-contained entrypoint.
But, I could be convinced to move them into react.dart
.
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 feel the same it makes sense to have them seperate but i also enjoy the convenience of getting them when importing react.dart
... plus they would get tree shaken out if they weren't used right?
lib/hooks.dart
Outdated
/// Updates [value] to the return value of [computeNewValue]. | ||
/// | ||
/// See: <https://reactjs.org/docs/hooks-reference.html#functional-updates>. | ||
void setTx(T computeNewValue(T oldValue)) => _setValue(allowInterop(computeNewValue)); |
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 does Tx
represent? Does this naming convention map to an analogous pattern in the JS world?
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 was mean to be "transactional", but it was clear when I had setStateTx
that that wasn't clear haha...
Perhaps setWithUpdater
would be better? Or even just update
? But with the latter it might not be the immediately obvious the difference between set
and update
.
Thoughts? I think that for these kinds of naming decisions, we should gather input from more rather than fewer people. @joebingham-wk @kealjones-wk
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 setWithUpdater
makes a lot of sense as it correlates to component2's new setStateWithUpdater
, also we never made a codemod for that did we .... should we?
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 setWithUpdater
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.
agreed, i also like, setWithUpdater
T _value; | ||
|
||
/// The second item in the pair returned by [React.useState]. | ||
void Function(dynamic) _setValue; |
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 believe the argument here should be typed as T
.
void Function(dynamic) _setValue; | |
void Function(T newValue) _setValue; |
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.
You might be right... my brain hurts when thinking about Dart 2 function types haha, so I'm not sure.
Works for me so long as there are no type errors in DDC or dart2js!
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.
Changing this type breaks the tests for setTx
. I think it's because _setValue
is the updater function returned by the JS useState and can take an argument of type T newValue
or type T computeNewValue(T oldValue)
.
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.
lib/hooks.dart
Outdated
/// return react.div({}, [ | ||
/// count.value, | ||
/// react.button({'onClick': (_) => count.set(0)}, ['Reset']), | ||
/// react.button({'onClick': (_) => count.setTx((prev) => prev + 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 this also possible without using setTx
?
react.button({'onClick': (_) => count.set(count.value + 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.
You're right, I just wrote it that way because that was the example of functional updates in the React docs:
https://reactjs.org/docs/hooks-reference.html#functional-updates
Should I change it to set
and use a different example for setTx
?
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 so, yeah.
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.
But we could also just make a follow-up ticket to improve the examples.
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.
Okay, I updated the example here
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 is actually a case where you would need to use setTx
.
Let's say that two setState calls were batched together:
react.button({'onClick': (_) {
count.setTx((prev) => prev + 1);
count.setTx((prev) => prev + 1);
}, ['+2']),
Clicking on the button would result in the value being incremented by two.
However, without setTx:
react.button({'onClick': (_) {
count.set(count.value + 1);
count.set(count.value + 1);
}, ['+2']),
Clicking on the button would result in the value being incremented by one, which is not expected.
Here, it would be best practice to use transactional setState calls.
React recommends using transactional state updaters where possible, and it also uses them in this corresponding example, so I think we should keep setTx
here
# Conflicts: # example/test/react_test_components.dart
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.
+10
count.value, | ||
react.button({'onClick': (_) => count.set(0)}, ['Reset']), | ||
react.button({ | ||
'onClick': (_) => count.setWithUpdater((prev) { |
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.
#nit it's better to avoid the state change altogether when disabled:
'onClick': (_) {
if (props['enabled']) {
count.setWithUpdater((prev) => prev + 1);
}
},
lib/hooks.dart
Outdated
/// count.value, | ||
/// react.button({'onClick': (_) => count.set(0)}, ['Reset']), | ||
/// react.button({ | ||
/// 'onClick': (_) => count.setWithUpdater((prev) { |
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.
#nit We should probably show avoiding the state change altogether when disabled:
'onClick': (_) {
if (props['enabled']) {
count.setWithUpdater((prev) => prev + 1);
}
},
Or for this example, just do:
'onClick': (_) => count.setWithUpdater((prev) => prev + 1)
+10 |
+1 |
+10'd above
Motivation
Support useState hook in react-dart
Changes
StateHook
classuseState
anduseStateLazy
functionsPlease review: @kealjones-wk @aaronlademann-wf @greglittlefield-wf @joebingham-wk