diff --git a/Libraries/Components/CheckBox/CheckBox.android.js b/Libraries/Components/CheckBox/CheckBox.android.js index 3b0973b8f..fe5e23eaf 100644 --- a/Libraries/Components/CheckBox/CheckBox.android.js +++ b/Libraries/Components/CheckBox/CheckBox.android.js @@ -9,21 +9,76 @@ */ 'use strict'; -const DeprecatedViewPropTypes = require('DeprecatedViewPropTypes'); -const NativeMethodsMixin = require('NativeMethodsMixin'); -const PropTypes = require('prop-types'); const React = require('React'); const StyleSheet = require('StyleSheet'); -const createReactClass = require('create-react-class'); const requireNativeComponent = require('requireNativeComponent'); +const nullthrows = require('nullthrows'); +const setAndForwardRef = require('setAndForwardRef'); -const RCTCheckBox = requireNativeComponent('AndroidCheckBox'); +import type {ViewProps} from 'ViewPropTypes'; +import type {SyntheticEvent} from 'CoreEventTypes'; +import type {NativeComponent} from 'ReactNative'; -type DefaultProps = { - value: boolean, - disabled: boolean, -}; +type CheckBoxEvent = SyntheticEvent< + $ReadOnly<{| + target: number, + value: boolean, + |}>, +>; + +type CommonProps = $ReadOnly<{| + ...ViewProps, + + /** + * Used in case the props change removes the component. + */ + onChange?: ?(event: CheckBoxEvent) => mixed, + + /** + * Invoked with the new value when the value changes. + */ + onValueChange?: ?(value: boolean) => mixed, + + /** + * Used to locate this view in end-to-end tests. + */ + testID?: ?string, +|}>; + +type NativeProps = $ReadOnly<{| + ...CommonProps, + + on?: ?boolean, + enabled?: boolean, +|}>; + +type CheckBoxNativeType = Class>; + +type Props = $ReadOnly<{| + ...CommonProps, + + /** + * The value of the checkbox. If true the checkbox will be turned on. + * Default value is false. + */ + value?: ?boolean, + + /** + * If true the user won't be able to toggle the checkbox. + * Default value is false. + */ + disabled?: ?boolean, + + /** + * Used to get the ref for the native checkbox + */ + forwardedRef?: ?React.Ref, +|}>; + +const RCTCheckBox = ((requireNativeComponent( + 'AndroidCheckBox', +): any): CheckBoxNativeType); /** * Renders a boolean input (Android only). @@ -80,85 +135,64 @@ type DefaultProps = { * @keyword checkbox * @keyword toggle */ -let CheckBox = createReactClass({ - displayName: 'CheckBox', - propTypes: { - ...DeprecatedViewPropTypes, - /** - * The value of the checkbox. If true the checkbox will be turned on. - * Default value is false. - */ - value: PropTypes.bool, - /** - * If true the user won't be able to toggle the checkbox. - * Default value is false. - */ - disabled: PropTypes.bool, - /** - * Used in case the props change removes the component. - */ - onChange: PropTypes.func, - /** - * Invoked with the new value when the value changes. - */ - onValueChange: PropTypes.func, - /** - * Used to locate this view in end-to-end tests. - */ - testID: PropTypes.string, - }, +class CheckBox extends React.Component { + _nativeRef: ?React.ElementRef = null; + _setNativeRef = setAndForwardRef({ + getForwardedRef: () => this.props.forwardedRef, + setLocalRef: ref => { + this._nativeRef = ref; + }, + }); - getDefaultProps: function(): DefaultProps { - return { - value: false, - disabled: false, - }; - }, - - mixins: [NativeMethodsMixin], - - _rctCheckBox: {}, - _onChange: function(event: Object) { - this._rctCheckBox.setNativeProps({value: this.props.value}); + _onChange = (event: CheckBoxEvent) => { + const value = this.props.value ?? false; + nullthrows(this._nativeRef).setNativeProps({value: value}); // Change the props after the native props are set in case the props // change removes the component this.props.onChange && this.props.onChange(event); this.props.onValueChange && this.props.onValueChange(event.nativeEvent.value); - }, + }; - render: function() { - let props = {...this.props}; - props.onStartShouldSetResponder = () => true; - props.onResponderTerminationRequest = () => false; - /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found - * when making Flow check .android.js files. */ - props.enabled = !this.props.disabled; - /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found - * when making Flow check .android.js files. */ - props.on = this.props.value; - props.style = [styles.rctCheckBox, this.props.style]; + render() { + const {disabled: _, value: __, style, forwardedRef, ...props} = this.props; + const disabled = this.props.disabled ?? false; + const value = this.props.value ?? false; + + const nativeProps = { + ...props, + onStartShouldSetResponder: () => true, + onResponderTerminationRequest: () => false, + enabled: !disabled, + on: value, + style: [styles.rctCheckBox, style], + }; return ( { - /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This - * comment suppresses an error when upgrading Flow's support for - * React. To see the error delete this comment and run Flow. */ - this._rctCheckBox = ref; - }} + {...nativeProps} + ref={this._setNativeRef} onChange={this._onChange} /> ); - }, -}); + } +} -let styles = StyleSheet.create({ +const styles = StyleSheet.create({ rctCheckBox: { height: 32, width: 32, }, }); -module.exports = CheckBox; +/** + * Can't use CheckBoxNativeType because it has different props + */ +type CheckBoxType = Class>; + +// $FlowFixMe - TODO T29156721 `React.forwardRef` is not defined in Flow, yet. +const CheckBoxWithRef = React.forwardRef(function CheckBoxWithRef(props, ref) { + return ; +}); + +module.exports = (CheckBoxWithRef: CheckBoxType);