-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Form - Migrate setNativeProps to state. #11039
Form - Migrate setNativeProps to state. #11039
Conversation
Hey! I see that you made changes to our Form component. Make sure to update the docs in FORMS.md accordingly. Cheers! |
return; | ||
} | ||
|
||
this.props.onInputChange(value, index); |
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.
Question from @parasharrajat - How is this change related to removing setNativeProps
?
Rushat:
Anytime a Form input's value is changed, onInputChange()
is called.
If you'll look at Form.js
Lines 161 to 166 in 97ebb65
onInputChange: (value, key) => { | |
const inputKey = key || inputID; | |
this.setState(prevState => ({ | |
inputValues: { | |
...prevState.inputValues, | |
[inputKey]: value, |
It keeps track of input values based on the key provided to it.
And this "key" is supposed to be the formID used by the Form input.
So our expected function call looks something like this - onInputChange('Portugal', 'countryPicker')
Prerequisite: Picker is used in a Form.
Now RNPickerSelect
comes into picture.
It passes the picker item's index as the second parameter. (source)
So this is what actually gets called - onInputChange('Portugal', 156)
. (where 156 is the index of Portugal in a list)
Our Form based picker now will try to change the value of an input whose formID is 156.
And bam 💥 our Form based picker doesn't work anymore.
I'm just fixing this by passing the formID as the second param, because we don't care aobut the index lol
This wasn't a problem before because Form previously set the value using ref.setNativeProps.
We're getting rid of that now. So we gotta fix it 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.
The explanation makes sense, but I'm not sure that this is necessary. From the Form docs, the intent of the second param is to:
Passing an inputID as a second param allows inputA to manipulate the input value of the provided inputID (inputB).
If you look at Form, we have const inputKey = key || inputID;
so if key
(the 2nd param) is not provided, we will automatically use the inputID
provided to that Picker. The only instance in which we want to pass a 2nd param is if that picker is also controlling the value for a different input. In which case we would provide a custom callback (not yet supported, see this issue) and pass the inputID
of the second input.
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.
@rushatgabhane what are your thoughts about my comment above?
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 started reading this, will get back soon
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.
Passing an inputID as a second param allows inputA to manipulate the input value of the provided inputID (inputB).
Thank you, I understand the use of 2nd parameter better now.
I've updated the call to not pass the 2nd parameter.
@luacmartins this is the PR you might be most interested in. I've addressed your comments from #10934 (review) |
This comment was marked as outdated.
This comment was marked as outdated.
First, we should create a new issue instead of using #8503. This PR has nothing to do with Fabric architecture technically. |
@parasharrajat this PR is a prerequisite step to enabling fabric right? |
I know but does it enable the Fabric architecture? |
Fair enough |
@parasharrajat @luacmartins are we going on the right track? I feel like uncontrolled Form was so much cleaner. You didn't need to maintain values in state for pages that are using Form. Is there a better approach? |
That was one of the reasons, we use uncontrolled inputs. 😄 |
I'm not sure there's much that we can do in this case. State seems to be the correct solution. |
Cool, glad to hear we're on the right track. P.S. Sorry but I'll add the screenshots to all the PRs within a week |
@rushatgabhane is this ready for review? |
ready for review |
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.
Run npm run storybook and test picker functionalities. Make sure that we can edit the input and all values are updated accordinly.
NAB just noting that the callback provided in Picker's story is an empty lambda and the value is hardcoded, so selecting a different value and blurring the input resets the value to the previous one.
I did find an issue while testing though. In Profile > Timezone
, manually selecting a different timezone and then checking Set my timezone automatically
doesn't update the timezone to the correct one. The first value loaded also is incorrect for me, it should be Americas/Edmonton
in my case.
Screen.Recording.2022-09-20.at.11.06.59.AM.mov
Thanks, I'm working on it |
return; | ||
} | ||
|
||
this.setState({hasSelfSelectedPronouns}); |
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.
NAB: This function is doing too many things.
But I'm not sure how to clean things up 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.
@rushatgabhane I did notice the following bugs while testing:
- Picker does not work on
Workspace > Connect bank account (Company step) > Company type
andWorkspace > Connect bank account (Company step) > Corporation state
error1.mov
- Picker is selecting the value before user clicks done on iOS:
error2.mov
Additionally, the bugs below seem to already be on prod 🤦
This seems to be fixed with the changes in this PR 🎉 |
…ensify-App into rm-setNativeProps-form
This was introduced by #11120 when I merged with Fixed the iOS issue and #10437 as well by removing they Screen.Recording.2022-10-07.at.2.06.38.AM.mov
Sorry about that, I missed Pickers in CompanyStep page. I went through all other pages to make sure it isn't happening elsewhere. |
This is resolved by this PR as well 🎉 Screen.Recording.2022-10-07.at.2.24.54.AM.mov |
@luacmartins could you please give this another look? All 4 bugs you had mentioned are now fixed. |
Should we apply CP-staging label on this PR since it addresses a deploy blocker issue. I think we should either CP this and then ask QA to regression test again since this changes a lot, OR ignore the deploy blocker for now since it's minor. Either way, I think regression testing should be done once this PR is on staging since its changes affect a number of components. Thoughts? |
@chiragsalian I think that we should remove the blocker label from that issue since it's a minor UI bug and have this go through the regular QA process and have it tested with full regression tests. |
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.
LGTM and tested well. Thanks for squashing all those bugs! Awesome job!
Wohoo! 🎉 |
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
🚀 Deployed to staging by @luacmartins in version: 1.2.13-0 🚀
|
🚀 Deployed to production by @yuwenmemon in version: 1.2.13-5 🚀
|
…ps-form Form - Migrate setNativeProps to state.
if (_.isUndefined(this.state.inputValues[inputID])) { | ||
this.state.inputValues[inputID] = defaultValue; | ||
} | ||
|
||
if (!_.isUndefined(child.props.value)) { | ||
this.state.inputValues[inputID] = child.props.value; |
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 just bumped into this code and noticed that we are mutating the state during rendering. I haven't seen any bug caused by this, but, as far as I know, a base assumption when working in React that the state won't mutate like this, otherwise, basic checks equality checks by reference become not reliable.
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 any strong reason to mutate the state in this case?
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 you're right that this shouldn't be done. I think the state will change, but we won't re-render. AFAIK we should always be using setState
cc @rushatgabhane
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 seems like this can be a technique to exactly do that, avoid re-rendering. In some cases it makes sense to avoid re-rendering.. but there are other ways of achieving this without having to mutate the state. There was a similar conversation here about a proposal suggesting to do this: #11086 (comment)
Summary:
- React documentation says we shouldn't mutate state: https://reactjs.org/docs/react-component.html#state
- To have a "state" you can change without re-rendering, I think the recommended way is:
- Class component: member variable
- Functional component:
useRef
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 any strong reason to mutate the state in this case?
Great question. I was aware that it isn't recommended to mutate state but chose to mutate because -
Lines 166 to 167 in c72e7ac
if (!_.isUndefined(child.props.value)) { | |
this.state.inputValues[inputID] = child.props.value; |
When value
is passed as a prop, the parent component is expected to maintain (init and update) the state and cause a re-render.
If we had used setState
, it'll cause an unnecessary re-render.
The form component also needs the correct inputValues
to call the validate function.
Line 176 in c72e7ac
this.validate(this.state.inputValues); |
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.
There are times when we need to trigger a re-render. eg: uncontrolled input changes. setState
is called for inputValues
Lines 178 to 181 in c72e7ac
onInputChange: (value, key) => { | |
const inputKey = key || inputID; | |
this.setState(prevState => ({ | |
inputValues: { |
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.
To have a "state" you can change without re-rendering, I think the recommended way is:
Class component: member variable
Agree. But why maintain another duplicate variable that we'll need to always keep in sync with inputValues
?
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.
To have a "state" you can change without re-rendering, I think the recommended way is:
Class component: member variableAgree. But why maintain another duplicate variable that we'll need to always keep in sync with
inputValues
?
I haven't really gone in depth in this code, and I don't really plan to at the moment because of priorities, but even in the worse case of having duplicate variables, I still think it is better that doing something that is not expected in the framework.
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.
Cool, I'll raise a PR to clean this up (hopefully by end of this week)
This way I can dive deep and see if it causes any bugs.
note to self: deep clone everything
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, thanks guys!
Details
setNativeProps
will not be supported in the post-Fabric world (docs). Removing it's usage is a prerequisite to enable Fabric in #8503 . I'm creating multiple PRs to migrate it's usage. This is one of many.Form
is made controlled, andPicker
's usage ofsetNativeProps
has been removed.Fixed Issues
$ #11049
$ #11646
$ #11654
$ #10437
Tests
Form component
npm run storybook
and test Form. Make sure that we can edit the input and all values are updated accordingly.Picker
npm run storybook
and test picker functionalities. Make sure that we can edit the input and all values are updated accordinly.Address search
Pages using Form.js
Blueline around the fields
PR Review Checklist
Contributor (PR Author) Checklist
### Fixed Issues
section aboveTests
sectionQA steps
sectiontoggleReport
and notonIconClick
)src/languages/*
filesSTYLE.md
) were followedAvatar
, I verified the components usingAvatar
are working as expected)/** comment above it */
displayName
propertythis
properly so there are no scoping issues (i.e. foronClick={this.submit}
the methodthis.submit
should be bound tothis
in the constructor)this
are necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);
ifthis.submit
is never passed to a component event handler likeonClick
)StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)PR Reviewer Checklist
The Contributor+ will copy/paste it into a new comment and complete it after the author checklist is completed
### Fixed Issues
section aboveTests
sectionQA steps
sectiontoggleReport
and notonIconClick
).src/languages/*
filesSTYLE.md
) were followedAvatar
, I verified the components usingAvatar
have been tested & I retested again)/** comment above it */
displayName
propertythis
properly so there are no scoping issues (i.e. foronClick={this.submit}
the methodthis.submit
should be bound tothis
in the constructor)this
are necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);
ifthis.submit
is never passed to a component event handler likeonClick
)StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)QA Steps
Form component
storybook
and test Form. Make sure that we can edit the input and all values are updated accordingly.Picker
npm run storybook
and test picker functionalities. Make sure that we can edit the input and all values are updated accordinly.Address search
Pages using Form.js
Blueline around the fields
Screenshots
Web
Form - Profile Page
Screen.Recording.2022-10-05.at.2.15.17.AM.mov
Form - Debit card page
Screen.Recording.2022-10-05.at.2.18.38.AM.mov
Non Form Picker - Preferences page
Screen.Recording.2022-10-05.at.2.37.04.AM.mov
Mobile Web
Screen.Recording.2022-10-05.at.2.34.09.AM.mov
Desktop
Working of the Address search component
Screen.Recording.2022-10-05.at.2.24.43.AM.mov
iOS
Android
screen-20221005-030018.mp4
Storybook
Screen.Recording.2022-10-05.at.3.02.08.AM.mov