Skip to content

Commit b782934

Browse files
rurikoarakifacebook-github-bot
authored andcommitted
Fix excessive toggles on the Switch component (#26496)
Summary: In Windows, if you clicked on a Switch component to toggle it, you could see it "shimmy" back and forth once before settling. The native Switch ends up toggling three times every time it's invoked. `Switch.js` prevents the native switch from toggling to the new value by explicitly setting the switch to `this.props.value` when it receives an `onChange` event. The re-setting of the value wasn't fast enough to prevent the `Switch` from starting to toggle, causing the visual shimmy. The solution is taken from `TextInput`. `TextInput.js` stores `_lastNativeText` when it receives an `onChange` event. In `componentDidUpdate`, it puts `this.props.text` back on the native textbox if the value of `this.props.text` isn't the same as `_lastNativeText`, which is how it ensures that it is a controlled component. Porting this to the `Switch` results in only one toggle happening per invoke, removing the shimmy, while preserving the controlled component behavior. This bug is not visible on Android or iOS, only Windows, however the code causing the bug was in `Switch.js` and it seems reasonable to avoid changing the value of the native switch excessively. ## Changelog [General] [Fixed] - Fix excessive toggles on the Switch component Pull Request resolved: #26496 Test Plan: Used RNTester on Android and iOS to test the Switch component and made sure that all scenarios behave as expected visually. Also ensured through the debugger that the value of the native switch is only being changed once, instead of three times. Reviewed By: TheSavior Differential Revision: D17468905 Pulled By: JoshuaGross fbshipit-source-id: 92bf511510306968c3573ee4eed6df009850fd77
1 parent 9f0dede commit b782934

File tree

1 file changed

+21
-9
lines changed

1 file changed

+21
-9
lines changed

Libraries/Components/Switch/Switch.js

+21-9
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class Switch extends React.Component<Props> {
9292
_nativeSwitchRef: ?React.ElementRef<
9393
typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent,
9494
>;
95+
_lastNativeValue: ?boolean;
9596

9697
render(): React.Node {
9798
const {
@@ -196,26 +197,37 @@ class Switch extends React.Component<Props> {
196197
);
197198
}
198199

199-
_handleChange = (event: SwitchChangeEvent) => {
200-
if (this._nativeSwitchRef == null) {
201-
return;
200+
componentDidUpdate() {
201+
// This is necessary in case native updates the switch and JS decides
202+
// that the update should be ignored and we should stick with the value
203+
// that we have in JS.
204+
const nativeProps = {};
205+
const value = this.props.value === true;
206+
207+
if (this._lastNativeValue !== value && typeof value === 'boolean') {
208+
nativeProps.value = value;
202209
}
203210

204-
// Force value of native switch in order to control it.
205-
const value = this.props.value === true;
206-
if (Platform.OS === 'android') {
207-
this._nativeSwitchRef.setNativeProps({on: value});
208-
} else {
209-
this._nativeSwitchRef.setNativeProps({value});
211+
if (
212+
Object.keys(nativeProps).length > 0 &&
213+
this._nativeSwitchRef &&
214+
this._nativeSwitchRef.setNativeProps
215+
) {
216+
this._nativeSwitchRef.setNativeProps(nativeProps);
210217
}
218+
}
211219

220+
_handleChange = (event: SwitchChangeEvent) => {
212221
if (this.props.onChange != null) {
213222
this.props.onChange(event);
214223
}
215224

216225
if (this.props.onValueChange != null) {
217226
this.props.onValueChange(event.nativeEvent.value);
218227
}
228+
229+
this._lastNativeValue = event.nativeEvent.value;
230+
this.forceUpdate();
219231
};
220232

221233
_handleSwitchNativeComponentRef = (

0 commit comments

Comments
 (0)