diff --git a/src/components/Picker/BasePicker.js b/src/components/Picker/BasePicker.js
index d7fbb2ed8d6b..173b863edfcc 100644
--- a/src/components/Picker/BasePicker.js
+++ b/src/components/Picker/BasePicker.js
@@ -1,5 +1,5 @@
 import _ from 'underscore';
-import React, {PureComponent} from 'react';
+import React, {useContext, useEffect, useImperativeHandle, useRef, useState} from 'react';
 import {View} from 'react-native';
 import PropTypes from 'prop-types';
 import RNPickerSelect from 'react-native-picker-select';
@@ -12,6 +12,9 @@ import themeColors from '../../styles/themes/default';
 import {ScrollContext} from '../ScrollViewWithContext';
 
 const propTypes = {
+    /** A forwarded ref */
+    forwardedRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({current: PropTypes.instanceOf(React.Component)})]),
+
     /** BasePicker label */
     label: PropTypes.string,
 
@@ -81,6 +84,7 @@ const propTypes = {
 };
 
 const defaultProps = {
+    forwardedRef: undefined,
     label: '',
     isDisabled: false,
     errorText: '',
@@ -104,204 +108,186 @@ const defaultProps = {
     additionalPickerEvents: () => {},
 };
 
-/**
- * @property {View} root - a reference to the root View
- * @property {Object} picker - a reference to @react-native-picker/picker
- */
-class BasePicker extends PureComponent {
-    constructor(props) {
-        super(props);
-        this.state = {
-            isHighlighted: false,
-        };
-
-        this.onInputChange = this.onInputChange.bind(this);
-        this.enableHighlight = this.enableHighlight.bind(this);
-        this.disableHighlight = this.disableHighlight.bind(this);
-        this.focus = this.focus.bind(this);
-        this.measureLayout = this.measureLayout.bind(this);
-
-        // Windows will reuse the text color of the select for each one of the options
-        // so we might need to color accordingly so it doesn't blend with the background.
-        this.placeholder = _.isEmpty(this.props.placeholder)
-            ? {}
-            : {
-                  ...this.props.placeholder,
-                  color: themeColors.pickerOptionsTextColor,
-              };
-    }
+function BasePicker(props) {
+    const [isHighlighted, setIsHighlighted] = useState(false);
 
-    componentDidMount() {
-        this.setDefaultValue();
-    }
+    // reference to the root View
+    const root = useRef(null);
 
-    componentDidUpdate(prevProps) {
-        if (prevProps.items === this.props.items) {
-            return;
-        }
-        this.setDefaultValue();
-    }
+    // reference to @react-native-picker/picker
+    const picker = useRef(null);
 
-    /**
-     * Forms use inputID to set values. But BasePicker passes an index as the second parameter to onInputChange
-     * We are overriding this behavior to make BasePicker work with Form
-     * @param {String} value
-     * @param {Number} index
-     */
-    onInputChange(value, index) {
-        if (this.props.inputID) {
-            this.props.onInputChange(value);
+    // Windows will reuse the text color of the select for each one of the options
+    // so we might need to color accordingly so it doesn't blend with the background.
+    const placeholder = _.isEmpty(props.placeholder)
+        ? {}
+        : {
+              ...props.placeholder,
+              color: themeColors.pickerOptionsTextColor,
+          };
+
+    useEffect(() => {
+        if (props.value || !props.items || props.items.length !== 1 || !props.onInputChange) {
             return;
         }
 
-        this.props.onInputChange(value, index);
-    }
-
-    setDefaultValue() {
         // When there is only 1 element in the selector, we do the user a favor and automatically select it for them
         // so they don't have to spend extra time selecting the only possible value.
-        if (this.props.value || !this.props.items || this.props.items.length !== 1 || !this.props.onInputChange) {
-            return;
-        }
-        this.props.onInputChange(this.props.items[0].value, 0);
-    }
+        props.onInputChange(props.items[0].value, 0);
 
-    enableHighlight() {
-        this.setState({
-            isHighlighted: true,
-        });
-    }
-
-    disableHighlight() {
-        this.setState({
-            isHighlighted: false,
-        });
-    }
+        // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [props.items]);
 
-    /**
-     * Focuses the picker (if configured to do so)
-     *
-     * This method is used by Form
-     */
-    focus() {
-        if (!this.props.shouldFocusPicker) {
-            return;
-        }
-
-        // Defer the focusing to work around a bug on Mobile Safari, where focusing the `select` element in the same
-        // task when we scrolled to it left that element in a glitched state, where the dropdown list can't be opened
-        // until the element gets re-focused
-        _.defer(() => {
-            this.picker.focus();
-        });
-    }
+    const context = useContext(ScrollContext);
 
     /**
-     * Like measure(), but measures the view relative to an ancestor
-     *
-     * This method is used by Form when scrolling to the input
-     *
-     * @param {Object} relativeToNativeComponentRef - reference to an ancestor
-     * @param {function(x: number, y: number, width: number, height: number): void} onSuccess - callback called on success
-     * @param {function(): void} onFail - callback called on failure
+     * Forms use inputID to set values. But BasePicker passes an index as the second parameter to onInputChange
+     * We are overriding this behavior to make BasePicker work with Form
+     * @param {String} value
+     * @param {Number} index
      */
-    measureLayout(relativeToNativeComponentRef, onSuccess, onFail) {
-        if (!this.root) {
+    const onInputChange = (value, index) => {
+        if (props.inputID) {
+            props.onInputChange(value);
             return;
         }
 
-        this.root.measureLayout(relativeToNativeComponentRef, onSuccess, onFail);
-    }
-
-    render() {
-        const hasError = !_.isEmpty(this.props.errorText);
-
-        if (this.props.isDisabled) {
-            return (
-                <View>
-                    {Boolean(this.props.label) && (
-                        <Text
-                            style={[styles.textLabelSupporting, styles.mb1]}
-                            numberOfLines={1}
-                        >
-                            {this.props.label}
-                        </Text>
-                    )}
-                    <Text numberOfLines={1}>{this.props.value}</Text>
-                    {Boolean(this.props.hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{this.props.hintText}</Text>}
-                </View>
-            );
-        }
-
+        props.onInputChange(value, index);
+    };
+
+    const enableHighlight = () => {
+        setIsHighlighted(true);
+    };
+
+    const disableHighlight = () => {
+        setIsHighlighted(false);
+    };
+
+    useImperativeHandle(props.forwardedRef, () => ({
+        /**
+         * Focuses the picker (if configured to do so)
+         *
+         * This method is used by Form
+         */
+        focus() {
+            if (!props.shouldFocusPicker) {
+                return;
+            }
+
+            // Defer the focusing to work around a bug on Mobile Safari, where focusing the `select` element in the
+            // same task when we scrolled to it left that element in a glitched state, where the dropdown list can't
+            // be opened until the element gets re-focused
+            _.defer(() => {
+                picker.current.focus();
+            });
+        },
+
+        /**
+         * Like measure(), but measures the view relative to an ancestor
+         *
+         * This method is used by Form when scrolling to the input
+         *
+         * @param {Object} relativeToNativeComponentRef - reference to an ancestor
+         * @param {function(x: number, y: number, width: number, height: number): void} onSuccess - callback called on success
+         * @param {function(): void} onFail - callback called on failure
+         */
+        measureLayout(relativeToNativeComponentRef, onSuccess, onFail) {
+            if (!root.current) {
+                return;
+            }
+
+            root.current.measureLayout(relativeToNativeComponentRef, onSuccess, onFail);
+        },
+    }));
+
+    const hasError = !_.isEmpty(props.errorText);
+
+    if (props.isDisabled) {
         return (
-            <>
-                <View
-                    ref={(el) => (this.root = el)}
-                    style={[
-                        styles.pickerContainer,
-                        this.props.isDisabled && styles.inputDisabled,
-                        ...this.props.containerStyles,
-                        this.state.isHighlighted && styles.borderColorFocus,
-                        hasError && styles.borderColorDanger,
-                    ]}
-                >
-                    {this.props.label && (
-                        <Text
-                            pointerEvents="none"
-                            style={[styles.pickerLabel, styles.textLabelSupporting]}
-                        >
-                            {this.props.label}
-                        </Text>
-                    )}
-                    <RNPickerSelect
-                        onValueChange={this.onInputChange}
-                        // We add a text color to prevent white text on white background dropdown items on Windows
-                        items={_.map(this.props.items, (item) => ({...item, color: themeColors.pickerOptionsTextColor}))}
-                        style={this.props.size === 'normal' ? styles.picker(this.props.isDisabled, this.props.backgroundColor) : styles.pickerSmall(this.props.backgroundColor)}
-                        useNativeAndroidPickerStyle={false}
-                        placeholder={this.placeholder}
-                        value={this.props.value}
-                        Icon={() => this.props.icon(this.props.size)}
-                        disabled={this.props.isDisabled}
-                        fixAndroidTouchableBug
-                        onOpen={this.enableHighlight}
-                        onClose={this.disableHighlight}
-                        textInputProps={{
-                            allowFontScaling: false,
-                        }}
-                        pickerProps={{
-                            ref: (el) => (this.picker = el),
-                            onFocus: this.enableHighlight,
-                            onBlur: () => {
-                                this.disableHighlight();
-                                this.props.onBlur();
-                            },
-                            ...this.props.additionalPickerEvents(this.enableHighlight, (value, index) => {
-                                this.onInputChange(value, index);
-                                this.disableHighlight();
-                            }),
-                        }}
-                        scrollViewRef={this.context && this.context.scrollViewRef}
-                        scrollViewContentOffsetY={this.context && this.context.contentOffsetY}
-                    />
-                </View>
-                <FormHelpMessage message={this.props.errorText} />
-                {Boolean(this.props.hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{this.props.hintText}</Text>}
-            </>
+            <View>
+                {Boolean(props.label) && (
+                    <Text
+                        style={[styles.textLabelSupporting, styles.mb1]}
+                        numberOfLines={1}
+                    >
+                        {props.label}
+                    </Text>
+                )}
+                <Text numberOfLines={1}>{props.value}</Text>
+                {Boolean(props.hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{props.hintText}</Text>}
+            </View>
         );
     }
+
+    return (
+        <>
+            <View
+                ref={root}
+                style={[
+                    styles.pickerContainer,
+                    props.isDisabled && styles.inputDisabled,
+                    ...props.containerStyles,
+                    isHighlighted && styles.borderColorFocus,
+                    hasError && styles.borderColorDanger,
+                ]}
+            >
+                {props.label && (
+                    <Text
+                        pointerEvents="none"
+                        style={[styles.pickerLabel, styles.textLabelSupporting]}
+                    >
+                        {props.label}
+                    </Text>
+                )}
+                <RNPickerSelect
+                    onValueChange={onInputChange}
+                    // We add a text color to prevent white text on white background dropdown items on Windows
+                    items={_.map(props.items, (item) => ({...item, color: themeColors.pickerOptionsTextColor}))}
+                    style={props.size === 'normal' ? styles.picker(props.isDisabled, props.backgroundColor) : styles.pickerSmall(props.backgroundColor)}
+                    useNativeAndroidPickerStyle={false}
+                    placeholder={placeholder}
+                    value={props.value}
+                    Icon={() => props.icon(props.size)}
+                    disabled={props.isDisabled}
+                    fixAndroidTouchableBug
+                    onOpen={enableHighlight}
+                    onClose={disableHighlight}
+                    textInputProps={{
+                        allowFontScaling: false,
+                    }}
+                    pickerProps={{
+                        ref: picker,
+                        onFocus: enableHighlight,
+                        onBlur: () => {
+                            disableHighlight();
+                            props.onBlur();
+                        },
+                        ...props.additionalPickerEvents(enableHighlight, (value, index) => {
+                            onInputChange(value, index);
+                            disableHighlight();
+                        }),
+                    }}
+                    scrollViewRef={context && context.scrollViewRef}
+                    scrollViewContentOffsetY={context && context.contentOffsetY}
+                />
+            </View>
+            <FormHelpMessage message={props.errorText} />
+            {Boolean(props.hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{props.hintText}</Text>}
+        </>
+    );
 }
 
 BasePicker.propTypes = propTypes;
 BasePicker.defaultProps = defaultProps;
-BasePicker.contextType = ScrollContext;
+BasePicker.displayName = 'BasePicker';
 
 export default React.forwardRef((props, ref) => (
     <BasePicker
         // eslint-disable-next-line react/jsx-props-no-spreading
         {...props}
         // Forward the ref to BasePicker, as we implement imperative methods there
-        ref={ref}
+        forwardedRef={ref}
+        // eslint-disable-next-line react/prop-types
         key={props.inputID}
     />
 ));
