From 4b962a59045001a58beeb7bffdec141e3585b13b Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Mon, 5 Jun 2023 14:11:44 -0400 Subject: [PATCH 01/14] Refactor the EmojiPicker to a functional component --- src/components/EmojiPicker/EmojiPicker.js | 219 ++++++++++------------ 1 file changed, 98 insertions(+), 121 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index 1e15e9e20bf3..281fd2293623 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useState, useEffect, useRef} from 'react'; import {Dimensions, Keyboard} from 'react-native'; import _ from 'underscore'; import EmojiPickerMenu from './EmojiPickerMenu'; @@ -10,166 +10,143 @@ const DEFAULT_ANCHOR_ORIGIN = { vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, }; -class EmojiPicker extends React.Component { - constructor(props) { - super(props); - - this.hideEmojiPicker = this.hideEmojiPicker.bind(this); - this.showEmojiPicker = this.showEmojiPicker.bind(this); - this.selectEmoji = this.selectEmoji.bind(this); - this.measureEmojiPopoverAnchorPosition = this.measureEmojiPopoverAnchorPosition.bind(this); - this.measureEmojiPopoverAnchorPositionAndUpdateState = this.measureEmojiPopoverAnchorPositionAndUpdateState.bind(this); - this.focusEmojiSearchInput = this.focusEmojiSearchInput.bind(this); - this.onModalHide = () => {}; - this.onEmojiSelected = () => {}; - - this.state = { - isEmojiPickerVisible: false, - - // The horizontal and vertical position (relative to the window) where the emoji popover will display. - emojiPopoverAnchorPosition: { - horizontal: 0, - vertical: 0, - }, - - emojiPopoverAnchorOrigin: DEFAULT_ANCHOR_ORIGIN, - }; +function EmojiPicker () { + const [isEmojiPickerVisible, setIsEmojiPickerVisible] = useState(false); + const [emojiPopoverAnchorPosition, setEmojiPopoverAnchorPosition] = useState({ + horizontal: 0, + vertical: 0, + }); + const [emojiPopoverAnchorOrigin, setEmojiPopoverAnchorOrigin] = useState(DEFAULT_ANCHOR_ORIGIN); + const [onModalHide, setOnModalHide] = useState(() => {}); + const [onEmojiSelected, setOnEmojiSelected] = useState(() => {}); + const [emojiPopoverAnchor, setEmojiPopoverAnchor] = useState(null); + const emojiSearchInput = useRef(); + + function measureEmojiPopoverAnchorPosition() { + return new Promise((resolve) => { + if (!emojiPopoverAnchor) { + return resolve({horizontal: 0, vertical: 0}); + } + emojiPopoverAnchor.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y})); + }); } - componentDidMount() { - this.emojiPopoverDimensionListener = Dimensions.addEventListener('change', this.measureEmojiPopoverAnchorPositionAndUpdateState); + function measureEmojiPopoverAnchorPositionAndUpdateState() { + measureEmojiPopoverAnchorPosition().then((value) => { + setEmojiPopoverAnchorPosition(value); + }); } - componentDidUpdate(prevProps, prevState) { - if (prevState.isEmojiPickerVisible === this.state.isEmojiPickerVisible || !this.state.isEmojiPickerVisible) { - return; + useEffect(() => { + if (isEmojiPickerVisible) { + Keyboard.dismiss(); } - // Dismiss the keyboard to provide a focus for the emoji picker to avoid selection issues. - Keyboard.dismiss(); - } - - componentWillUnmount() { - if (!this.emojiPopoverDimensionListener) { - return; + const emojiPopoverDimensionListener = Dimensions.addEventListener('change', measureEmojiPopoverAnchorPositionAndUpdateState); + return () => { + emojiPopoverDimensionListener.remove(); } - this.emojiPopoverDimensionListener.remove(); - } + }, [isEmojiPickerVisible]); /** - * Callback for the emoji picker to add whatever emoji is chosen into the main input + * Hide the emoji picker menu. * - * @param {String} emoji - * @param {Object} emojiObject + * @param {Boolean} isNavigating */ - selectEmoji(emoji, emojiObject) { - // Prevent fast click / multiple emoji selection; - // The first click will hide the emoji picker by calling the hideEmojiPicker() function - // and in that function the emojiPopoverAnchor prop to will be set to null (synchronously) - // thus we rely on that prop to prevent fast click / multiple emoji selection - if (!this.emojiPopoverAnchor) { - return; - } - - this.hideEmojiPicker(); - if (_.isFunction(this.onEmojiSelected)) { - this.onEmojiSelected(emoji, emojiObject); + const hideEmojiPicker = (isNavigating) => { + if (isNavigating) { + setOnModalHide( () => {}); } + setEmojiPopoverAnchor(null); + setIsEmojiPickerVisible(false); } /** - * Hide the emoji picker menu. - * - * @param {Boolean} isNavigating + * Focus the search input in the emoji picker. */ - hideEmojiPicker(isNavigating) { - if (isNavigating) { - this.onModalHide = () => {}; + const focusEmojiSearchInput = () => { + if (!emojiSearchInput) { + return; } - this.emojiPopoverAnchor = null; - this.setState({isEmojiPickerVisible: false}); + emojiSearchInput.focus(); } /** * Show the emoji picker menu. * - * @param {Function} [onModalHide=() => {}] - Run a callback when Modal hides. - * @param {Function} [onEmojiSelected=() => {}] - Run a callback when Emoji selected. - * @param {Element} emojiPopoverAnchor - Element to which Popover is anchored + * @param {Function} [onModalHideValue=() => {}] - Run a callback when Modal hides. + * @param {Function} [onEmojiSelectedValue=() => {}] - Run a callback when Emoji selected. + * @param {Element} emojiPopoverAnchorValue - Element to which Popover is anchored * @param {Object} [anchorOrigin=DEFAULT_ANCHOR_ORIGIN] - Anchor origin for Popover * @param {Function} [onWillShow=() => {}] - Run a callback when Popover will show */ - showEmojiPicker(onModalHide, onEmojiSelected, emojiPopoverAnchor, anchorOrigin, onWillShow = () => {}) { - this.onModalHide = onModalHide; - this.onEmojiSelected = onEmojiSelected; - this.emojiPopoverAnchor = emojiPopoverAnchor; + function showEmojiPicker(onModalHideValue, onEmojiSelectedValue, emojiPopoverAnchorValue, anchorOrigin, onWillShow = () => {}) { + setOnModalHide(onModalHideValue); + setOnEmojiSelected(onEmojiSelectedValue); + setEmojiPopoverAnchor(emojiPopoverAnchorValue); - if (this.emojiPopoverAnchor) { + if (emojiPopoverAnchor) { // Drop focus to avoid blue focus ring. emojiPopoverAnchor.blur(); } - this.measureEmojiPopoverAnchorPosition().then((emojiPopoverAnchorPosition) => { + measureEmojiPopoverAnchorPosition().then((value) => { onWillShow(); - this.setState({isEmojiPickerVisible: true, emojiPopoverAnchorPosition, emojiPopoverAnchorOrigin: anchorOrigin || DEFAULT_ANCHOR_ORIGIN}); - }); - } - - measureEmojiPopoverAnchorPosition() { - return new Promise((resolve) => { - if (!this.emojiPopoverAnchor) { - return resolve({horizontal: 0, vertical: 0}); - } - this.emojiPopoverAnchor.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y})); - }); - } - - measureEmojiPopoverAnchorPositionAndUpdateState() { - this.measureEmojiPopoverAnchorPosition().then((emojiPopoverAnchorPosition) => { - this.setState({emojiPopoverAnchorPosition}); + setIsEmojiPickerVisible(true); + setEmojiPopoverAnchorPosition(value); + setEmojiPopoverAnchorOrigin(anchorOrigin || DEFAULT_ANCHOR_ORIGIN); }); } /** - * Focus the search input in the emoji picker. + * Callback for the emoji picker to add whatever emoji is chosen into the main input + * + * @param {String} emoji + * @param {Object} emojiObject */ - focusEmojiSearchInput() { - if (!this.emojiSearchInput) { + const selectEmoji = (emoji, emojiObject) => { + // Prevent fast click / multiple emoji selection; + // The first click will hide the emoji picker by calling the hideEmojiPicker() function + // and in that function the emojiPopoverAnchor prop to will be set to null (synchronously) + // thus we rely on that prop to prevent fast click / multiple emoji selection + if (!emojiPopoverAnchor) { return; } - this.emojiSearchInput.focus(); - } - render() { - // There is no way to disable animations and they are really laggy, because there are so many - // emojis. The best alternative is to set it to 1ms so it just "pops" in and out - return ( - - (this.emojiSearchInput = el)} - /> - - ); + hideEmojiPicker(); + if (_.isFunction(onEmojiSelected)) { + onEmojiSelected(emoji, emojiObject); + } } + + // There is no way to disable animations and they are really laggy, because there are so many + // emojis. The best alternative is to set it to 1ms so it just "pops" in and out + return ( + + + + ); } export default EmojiPicker; From 22775244f66aea769ad91b18e4020e6c34b16dc2 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Mon, 5 Jun 2023 16:50:24 -0400 Subject: [PATCH 02/14] Fix some weirdness with state --- src/components/EmojiPicker/EmojiPicker.js | 78 ++++++++++++----------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index 281fd2293623..ba75a9053fd5 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -1,4 +1,4 @@ -import React, {useState, useEffect, useRef} from 'react'; +import React, {useState, useEffect, useRef, forwardRef, useImperativeHandle} from 'react'; import {Dimensions, Keyboard} from 'react-native'; import _ from 'underscore'; import EmojiPickerMenu from './EmojiPickerMenu'; @@ -10,7 +10,7 @@ const DEFAULT_ANCHOR_ORIGIN = { vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, }; -function EmojiPicker () { +const EmojiPicker = forwardRef((props, ref) => { const [isEmojiPickerVisible, setIsEmojiPickerVisible] = useState(false); const [emojiPopoverAnchorPosition, setEmojiPopoverAnchorPosition] = useState({ horizontal: 0, @@ -22,17 +22,44 @@ function EmojiPicker () { const [emojiPopoverAnchor, setEmojiPopoverAnchor] = useState(null); const emojiSearchInput = useRef(); - function measureEmojiPopoverAnchorPosition() { + function measureEmojiPopoverAnchorPosition(anchorComponent) { return new Promise((resolve) => { - if (!emojiPopoverAnchor) { + if (!anchorComponent) { return resolve({horizontal: 0, vertical: 0}); } - emojiPopoverAnchor.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y})); + anchorComponent.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y})); + }); + } + + /** + * Show the emoji picker menu. + * + * @param {Function} [onModalHideValue=() => {}] - Run a callback when Modal hides. + * @param {Function} [onEmojiSelectedValue=() => {}] - Run a callback when Emoji selected. + * @param {Element} emojiPopoverAnchorValue - Element to which Popover is anchored + * @param {Object} [anchorOrigin=DEFAULT_ANCHOR_ORIGIN] - Anchor origin for Popover + * @param {Function} [onWillShow=() => {}] - Run a callback when Popover will show + */ + function showEmojiPicker(onModalHideValue, onEmojiSelectedValue, emojiPopoverAnchorValue, anchorOrigin, onWillShow = () => {}) { + setOnModalHide(() => onModalHideValue); + setOnEmojiSelected(() => onEmojiSelectedValue); + setEmojiPopoverAnchor(emojiPopoverAnchorValue); + + if (emojiPopoverAnchorValue) { + // Drop focus to avoid blue focus ring. + emojiPopoverAnchorValue.blur(); + } + + measureEmojiPopoverAnchorPosition(emojiPopoverAnchorValue).then((value) => { + onWillShow(); + setIsEmojiPickerVisible(true); + setEmojiPopoverAnchorPosition(value); + setEmojiPopoverAnchorOrigin(anchorOrigin || DEFAULT_ANCHOR_ORIGIN); }); } function measureEmojiPopoverAnchorPositionAndUpdateState() { - measureEmojiPopoverAnchorPosition().then((value) => { + measureEmojiPopoverAnchorPosition(emojiPopoverAnchor).then((value) => { setEmojiPopoverAnchorPosition(value); }); } @@ -46,7 +73,7 @@ function EmojiPicker () { return () => { emojiPopoverDimensionListener.remove(); } - }, [isEmojiPickerVisible]); + }, [isEmojiPickerVisible, emojiPopoverAnchor]); /** * Hide the emoji picker menu. @@ -65,37 +92,10 @@ function EmojiPicker () { * Focus the search input in the emoji picker. */ const focusEmojiSearchInput = () => { - if (!emojiSearchInput) { + if (!emojiSearchInput.current) { return; } - emojiSearchInput.focus(); - } - - /** - * Show the emoji picker menu. - * - * @param {Function} [onModalHideValue=() => {}] - Run a callback when Modal hides. - * @param {Function} [onEmojiSelectedValue=() => {}] - Run a callback when Emoji selected. - * @param {Element} emojiPopoverAnchorValue - Element to which Popover is anchored - * @param {Object} [anchorOrigin=DEFAULT_ANCHOR_ORIGIN] - Anchor origin for Popover - * @param {Function} [onWillShow=() => {}] - Run a callback when Popover will show - */ - function showEmojiPicker(onModalHideValue, onEmojiSelectedValue, emojiPopoverAnchorValue, anchorOrigin, onWillShow = () => {}) { - setOnModalHide(onModalHideValue); - setOnEmojiSelected(onEmojiSelectedValue); - setEmojiPopoverAnchor(emojiPopoverAnchorValue); - - if (emojiPopoverAnchor) { - // Drop focus to avoid blue focus ring. - emojiPopoverAnchor.blur(); - } - - measureEmojiPopoverAnchorPosition().then((value) => { - onWillShow(); - setIsEmojiPickerVisible(true); - setEmojiPopoverAnchorPosition(value); - setEmojiPopoverAnchorOrigin(anchorOrigin || DEFAULT_ANCHOR_ORIGIN); - }); + emojiSearchInput.current.focus(); } /** @@ -119,6 +119,8 @@ function EmojiPicker () { } } + useImperativeHandle(ref, () => ({showEmojiPicker})); + // There is no way to disable animations and they are really laggy, because there are so many // emojis. The best alternative is to set it to 1ms so it just "pops" in and out return ( @@ -143,10 +145,10 @@ function EmojiPicker () { > emojiSearchInput.current = el} /> ); -} +}); export default EmojiPicker; From b5198545372ad32236aeea3252fbe21afc675564 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Mon, 5 Jun 2023 17:07:32 -0400 Subject: [PATCH 03/14] prettier --- src/components/EmojiPicker/EmojiPicker.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index ba75a9053fd5..def7f0ec834e 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -72,8 +72,8 @@ const EmojiPicker = forwardRef((props, ref) => { const emojiPopoverDimensionListener = Dimensions.addEventListener('change', measureEmojiPopoverAnchorPositionAndUpdateState); return () => { emojiPopoverDimensionListener.remove(); - } - }, [isEmojiPickerVisible, emojiPopoverAnchor]); + }; + }, [isEmojiPickerVisible, emojiPopoverAnchor, measureEmojiPopoverAnchorPositionAndUpdateState]); /** * Hide the emoji picker menu. @@ -82,11 +82,11 @@ const EmojiPicker = forwardRef((props, ref) => { */ const hideEmojiPicker = (isNavigating) => { if (isNavigating) { - setOnModalHide( () => {}); + setOnModalHide(() => {}); } setEmojiPopoverAnchor(null); setIsEmojiPickerVisible(false); - } + }; /** * Focus the search input in the emoji picker. @@ -96,7 +96,7 @@ const EmojiPicker = forwardRef((props, ref) => { return; } emojiSearchInput.current.focus(); - } + }; /** * Callback for the emoji picker to add whatever emoji is chosen into the main input @@ -117,7 +117,7 @@ const EmojiPicker = forwardRef((props, ref) => { if (_.isFunction(onEmojiSelected)) { onEmojiSelected(emoji, emojiObject); } - } + }; useImperativeHandle(ref, () => ({showEmojiPicker})); @@ -145,7 +145,7 @@ const EmojiPicker = forwardRef((props, ref) => { > emojiSearchInput.current = el} + ref={(el) => (emojiSearchInput.current = el)} /> ); From 3a060108aa68338e6625336d8616a50a5525f111 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Mon, 5 Jun 2023 17:17:32 -0400 Subject: [PATCH 04/14] more JSX linting --- src/components/EmojiPicker/EmojiPicker.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index def7f0ec834e..6fea7300463a 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -58,22 +58,20 @@ const EmojiPicker = forwardRef((props, ref) => { }); } - function measureEmojiPopoverAnchorPositionAndUpdateState() { - measureEmojiPopoverAnchorPosition(emojiPopoverAnchor).then((value) => { - setEmojiPopoverAnchorPosition(value); - }); - } - useEffect(() => { if (isEmojiPickerVisible) { Keyboard.dismiss(); } - const emojiPopoverDimensionListener = Dimensions.addEventListener('change', measureEmojiPopoverAnchorPositionAndUpdateState); + const emojiPopoverDimensionListener = Dimensions.addEventListener('change', () => { + measureEmojiPopoverAnchorPosition(emojiPopoverAnchor).then((value) => { + setEmojiPopoverAnchorPosition(value); + }); + }); return () => { emojiPopoverDimensionListener.remove(); }; - }, [isEmojiPickerVisible, emojiPopoverAnchor, measureEmojiPopoverAnchorPositionAndUpdateState]); + }, [isEmojiPickerVisible, emojiPopoverAnchor]); /** * Hide the emoji picker menu. From 5cfcd993cc591f53c65017220fa506e5746b25a9 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Tue, 6 Jun 2023 16:35:23 -0400 Subject: [PATCH 05/14] PR comments --- src/components/EmojiPicker/EmojiPicker.js | 100 +++++++++++----------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index 6fea7300463a..fe9ccaee7e07 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -1,4 +1,4 @@ -import React, {useState, useEffect, useRef, forwardRef, useImperativeHandle} from 'react'; +import React, {useState, useEffect, useRef, forwardRef, useImperativeHandle, useCallback} from 'react'; import {Dimensions, Keyboard} from 'react-native'; import _ from 'underscore'; import EmojiPickerMenu from './EmojiPickerMenu'; @@ -10,26 +10,41 @@ const DEFAULT_ANCHOR_ORIGIN = { vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, }; +function measureEmojiPopoverAnchorPosition(anchorComponent) { + return new Promise((resolve) => { + if (!anchorComponent) { + return resolve({horizontal: 0, vertical: 0}); + } + anchorComponent.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y})); + }); +} + const EmojiPicker = forwardRef((props, ref) => { const [isEmojiPickerVisible, setIsEmojiPickerVisible] = useState(false); const [emojiPopoverAnchorPosition, setEmojiPopoverAnchorPosition] = useState({ horizontal: 0, vertical: 0, }); - const [emojiPopoverAnchorOrigin, setEmojiPopoverAnchorOrigin] = useState(DEFAULT_ANCHOR_ORIGIN); - const [onModalHide, setOnModalHide] = useState(() => {}); - const [onEmojiSelected, setOnEmojiSelected] = useState(() => {}); - const [emojiPopoverAnchor, setEmojiPopoverAnchor] = useState(null); + const emojiPopoverAnchorOrigin = useRef(DEFAULT_ANCHOR_ORIGIN); + const emojiPopoverAnchor = useRef(null); + const onModalHide = useRef(() => {}); + const onEmojiSelected = useRef(() => {}); const emojiSearchInput = useRef(); - function measureEmojiPopoverAnchorPosition(anchorComponent) { - return new Promise((resolve) => { - if (!anchorComponent) { - return resolve({horizontal: 0, vertical: 0}); - } - anchorComponent.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y})); + useEffect(() => { + if (isEmojiPickerVisible) { + Keyboard.dismiss(); + } + + const emojiPopoverDimensionListener = Dimensions.addEventListener('change', () => { + measureEmojiPopoverAnchorPosition(emojiPopoverAnchor.current).then((value) => { + setEmojiPopoverAnchorPosition(value); + }); }); - } + return () => { + emojiPopoverDimensionListener.remove(); + }; + }, [isEmojiPickerVisible]); /** * Show the emoji picker menu. @@ -40,61 +55,46 @@ const EmojiPicker = forwardRef((props, ref) => { * @param {Object} [anchorOrigin=DEFAULT_ANCHOR_ORIGIN] - Anchor origin for Popover * @param {Function} [onWillShow=() => {}] - Run a callback when Popover will show */ - function showEmojiPicker(onModalHideValue, onEmojiSelectedValue, emojiPopoverAnchorValue, anchorOrigin, onWillShow = () => {}) { - setOnModalHide(() => onModalHideValue); - setOnEmojiSelected(() => onEmojiSelectedValue); - setEmojiPopoverAnchor(emojiPopoverAnchorValue); + const showEmojiPicker = useCallback((onModalHideValue, onEmojiSelectedValue, emojiPopoverAnchorValue, anchorOrigin, onWillShow = () => {}) => { + onModalHide.current = onModalHideValue; + onEmojiSelected.current = onEmojiSelectedValue; + emojiPopoverAnchor.current = emojiPopoverAnchorValue; - if (emojiPopoverAnchorValue) { + if (emojiPopoverAnchor.current) { // Drop focus to avoid blue focus ring. - emojiPopoverAnchorValue.blur(); + emojiPopoverAnchor.current.blur(); } - measureEmojiPopoverAnchorPosition(emojiPopoverAnchorValue).then((value) => { + measureEmojiPopoverAnchorPosition(emojiPopoverAnchor.current).then((value) => { onWillShow(); setIsEmojiPickerVisible(true); setEmojiPopoverAnchorPosition(value); - setEmojiPopoverAnchorOrigin(anchorOrigin || DEFAULT_ANCHOR_ORIGIN); + emojiPopoverAnchorOrigin.current = anchorOrigin || DEFAULT_ANCHOR_ORIGIN; }); - } - - useEffect(() => { - if (isEmojiPickerVisible) { - Keyboard.dismiss(); - } - - const emojiPopoverDimensionListener = Dimensions.addEventListener('change', () => { - measureEmojiPopoverAnchorPosition(emojiPopoverAnchor).then((value) => { - setEmojiPopoverAnchorPosition(value); - }); - }); - return () => { - emojiPopoverDimensionListener.remove(); - }; - }, [isEmojiPickerVisible, emojiPopoverAnchor]); + }); /** * Hide the emoji picker menu. * * @param {Boolean} isNavigating */ - const hideEmojiPicker = (isNavigating) => { + const hideEmojiPicker = useCallback((isNavigating) => { if (isNavigating) { - setOnModalHide(() => {}); + onModalHide.current = () => {}; } - setEmojiPopoverAnchor(null); + emojiPopoverAnchor.current = null; setIsEmojiPickerVisible(false); - }; + }); /** * Focus the search input in the emoji picker. */ - const focusEmojiSearchInput = () => { + const focusEmojiSearchInput = useCallback(() => { if (!emojiSearchInput.current) { return; } emojiSearchInput.current.focus(); - }; + }); /** * Callback for the emoji picker to add whatever emoji is chosen into the main input @@ -102,20 +102,20 @@ const EmojiPicker = forwardRef((props, ref) => { * @param {String} emoji * @param {Object} emojiObject */ - const selectEmoji = (emoji, emojiObject) => { + const selectEmoji = useCallback((emoji, emojiObject) => { // Prevent fast click / multiple emoji selection; // The first click will hide the emoji picker by calling the hideEmojiPicker() function - // and in that function the emojiPopoverAnchor prop to will be set to null (synchronously) + // and in that function the emojiPopoverAnchor ref to will be set to null (synchronously) // thus we rely on that prop to prevent fast click / multiple emoji selection - if (!emojiPopoverAnchor) { + if (!emojiPopoverAnchor.current) { return; } hideEmojiPicker(); - if (_.isFunction(onEmojiSelected)) { - onEmojiSelected(emoji, emojiObject); + if (_.isFunction(onEmojiSelected.current)) { + onEmojiSelected.current(emoji, emojiObject); } - }; + }); useImperativeHandle(ref, () => ({showEmojiPicker})); @@ -126,7 +126,7 @@ const EmojiPicker = forwardRef((props, ref) => { isVisible={isEmojiPickerVisible} onClose={hideEmojiPicker} onModalShow={focusEmojiSearchInput} - onModalHide={onModalHide} + onModalHide={onModalHide.current} hideModalContentWhileAnimating shouldSetModalVisibility={false} animationInTiming={1} @@ -139,7 +139,7 @@ const EmojiPicker = forwardRef((props, ref) => { width: CONST.EMOJI_PICKER_SIZE.WIDTH, height: CONST.EMOJI_PICKER_SIZE.HEIGHT, }} - anchorAlignment={emojiPopoverAnchorOrigin} + anchorAlignment={emojiPopoverAnchorOrigin.current} > Date: Tue, 6 Jun 2023 17:01:17 -0400 Subject: [PATCH 06/14] Remove useCallbacks because we're using refs --- src/components/EmojiPicker/EmojiPicker.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index fe9ccaee7e07..160f6f2cd1fe 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -1,4 +1,4 @@ -import React, {useState, useEffect, useRef, forwardRef, useImperativeHandle, useCallback} from 'react'; +import React, {useState, useEffect, useRef, forwardRef, useImperativeHandle} from 'react'; import {Dimensions, Keyboard} from 'react-native'; import _ from 'underscore'; import EmojiPickerMenu from './EmojiPickerMenu'; @@ -55,7 +55,7 @@ const EmojiPicker = forwardRef((props, ref) => { * @param {Object} [anchorOrigin=DEFAULT_ANCHOR_ORIGIN] - Anchor origin for Popover * @param {Function} [onWillShow=() => {}] - Run a callback when Popover will show */ - const showEmojiPicker = useCallback((onModalHideValue, onEmojiSelectedValue, emojiPopoverAnchorValue, anchorOrigin, onWillShow = () => {}) => { + const showEmojiPicker = (onModalHideValue, onEmojiSelectedValue, emojiPopoverAnchorValue, anchorOrigin, onWillShow = () => {}) => { onModalHide.current = onModalHideValue; onEmojiSelected.current = onEmojiSelectedValue; emojiPopoverAnchor.current = emojiPopoverAnchorValue; @@ -71,30 +71,30 @@ const EmojiPicker = forwardRef((props, ref) => { setEmojiPopoverAnchorPosition(value); emojiPopoverAnchorOrigin.current = anchorOrigin || DEFAULT_ANCHOR_ORIGIN; }); - }); + }; /** * Hide the emoji picker menu. * * @param {Boolean} isNavigating */ - const hideEmojiPicker = useCallback((isNavigating) => { + const hideEmojiPicker = (isNavigating) => { if (isNavigating) { onModalHide.current = () => {}; } emojiPopoverAnchor.current = null; setIsEmojiPickerVisible(false); - }); + }; /** * Focus the search input in the emoji picker. */ - const focusEmojiSearchInput = useCallback(() => { + const focusEmojiSearchInput = () => { if (!emojiSearchInput.current) { return; } emojiSearchInput.current.focus(); - }); + }; /** * Callback for the emoji picker to add whatever emoji is chosen into the main input @@ -102,7 +102,7 @@ const EmojiPicker = forwardRef((props, ref) => { * @param {String} emoji * @param {Object} emojiObject */ - const selectEmoji = useCallback((emoji, emojiObject) => { + const selectEmoji = (emoji, emojiObject) => { // Prevent fast click / multiple emoji selection; // The first click will hide the emoji picker by calling the hideEmojiPicker() function // and in that function the emojiPopoverAnchor ref to will be set to null (synchronously) @@ -115,7 +115,7 @@ const EmojiPicker = forwardRef((props, ref) => { if (_.isFunction(onEmojiSelected.current)) { onEmojiSelected.current(emoji, emojiObject); } - }); + }; useImperativeHandle(ref, () => ({showEmojiPicker})); From 0c50bdaf1ebc36290873ea364b8f7381cd85a37e Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Wed, 7 Jun 2023 10:14:25 -0400 Subject: [PATCH 07/14] Move calculateAnchorPosition to component utils --- src/components/EmojiPicker/EmojiPicker.js | 14 +++----------- src/libs/ComponentUtils/index.js | 17 ++++++++++++++++- src/libs/ComponentUtils/index.native.js | 17 ++++++++++++++++- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index 160f6f2cd1fe..0bcd214bc287 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -4,21 +4,13 @@ import _ from 'underscore'; import EmojiPickerMenu from './EmojiPickerMenu'; import CONST from '../../CONST'; import PopoverWithMeasuredContent from '../PopoverWithMeasuredContent'; +import * as ComponentUtils from '../../libs/ComponentUtils'; const DEFAULT_ANCHOR_ORIGIN = { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, }; -function measureEmojiPopoverAnchorPosition(anchorComponent) { - return new Promise((resolve) => { - if (!anchorComponent) { - return resolve({horizontal: 0, vertical: 0}); - } - anchorComponent.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y})); - }); -} - const EmojiPicker = forwardRef((props, ref) => { const [isEmojiPickerVisible, setIsEmojiPickerVisible] = useState(false); const [emojiPopoverAnchorPosition, setEmojiPopoverAnchorPosition] = useState({ @@ -37,7 +29,7 @@ const EmojiPicker = forwardRef((props, ref) => { } const emojiPopoverDimensionListener = Dimensions.addEventListener('change', () => { - measureEmojiPopoverAnchorPosition(emojiPopoverAnchor.current).then((value) => { + ComponentUtils.calculateAnchorPosition(emojiPopoverAnchor.current).then((value) => { setEmojiPopoverAnchorPosition(value); }); }); @@ -65,7 +57,7 @@ const EmojiPicker = forwardRef((props, ref) => { emojiPopoverAnchor.current.blur(); } - measureEmojiPopoverAnchorPosition(emojiPopoverAnchor.current).then((value) => { + ComponentUtils.calculateAnchorPosition(emojiPopoverAnchor.current).then((value) => { onWillShow(); setIsEmojiPickerVisible(true); setEmojiPopoverAnchorPosition(value); diff --git a/src/libs/ComponentUtils/index.js b/src/libs/ComponentUtils/index.js index 319926911c67..892756490a96 100644 --- a/src/libs/ComponentUtils/index.js +++ b/src/libs/ComponentUtils/index.js @@ -5,4 +5,19 @@ const PASSWORD_AUTOCOMPLETE_TYPE = 'current-password'; const NEW_PASSWORD_AUTOCOMPLETE_TYPE = 'new-password'; const ACCESSIBILITY_ROLE_FORM = 'form'; -export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE}; +/** + * Gets the x,y position of the passed in component for the purpose of anchoring another component to it. + * + * @param anchorComponent + * @return {Promise} + */ +function calculateAnchorPosition(anchorComponent) { + return new Promise((resolve) => { + if (!anchorComponent) { + return resolve({horizontal: 0, vertical: 0}); + } + anchorComponent.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y})); + }); +} + +export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE, calculateAnchorPosition}; diff --git a/src/libs/ComponentUtils/index.native.js b/src/libs/ComponentUtils/index.native.js index ed38345cbe2b..d5d48c96a94a 100644 --- a/src/libs/ComponentUtils/index.native.js +++ b/src/libs/ComponentUtils/index.native.js @@ -2,4 +2,19 @@ const PASSWORD_AUTOCOMPLETE_TYPE = 'password'; const NEW_PASSWORD_AUTOCOMPLETE_TYPE = 'password-new'; const ACCESSIBILITY_ROLE_FORM = 'none'; -export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE}; +/** + * Gets the x,y position of the passed in component for the purpose of anchoring another component to it. + * + * @param anchorComponent + * @return {Promise} + */ +function calculateAnchorPosition(anchorComponent) { + return new Promise((resolve) => { + if (!anchorComponent) { + return resolve({horizontal: 0, vertical: 0}); + } + anchorComponent.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y})); + }); +} + +export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE, calculateAnchorPosition}; From fc364667e787fff07679baec68731f8d1ce472be Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Wed, 7 Jun 2023 11:03:35 -0400 Subject: [PATCH 08/14] JS Doc --- src/libs/ComponentUtils/index.js | 2 +- src/libs/ComponentUtils/index.native.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ComponentUtils/index.js b/src/libs/ComponentUtils/index.js index 892756490a96..8fc484f1768e 100644 --- a/src/libs/ComponentUtils/index.js +++ b/src/libs/ComponentUtils/index.js @@ -8,7 +8,7 @@ const ACCESSIBILITY_ROLE_FORM = 'form'; /** * Gets the x,y position of the passed in component for the purpose of anchoring another component to it. * - * @param anchorComponent + * @param {Element} anchorComponent * @return {Promise} */ function calculateAnchorPosition(anchorComponent) { diff --git a/src/libs/ComponentUtils/index.native.js b/src/libs/ComponentUtils/index.native.js index d5d48c96a94a..20a511bf2f51 100644 --- a/src/libs/ComponentUtils/index.native.js +++ b/src/libs/ComponentUtils/index.native.js @@ -5,7 +5,7 @@ const ACCESSIBILITY_ROLE_FORM = 'none'; /** * Gets the x,y position of the passed in component for the purpose of anchoring another component to it. * - * @param anchorComponent + * @param {Element} anchorComponent * @return {Promise} */ function calculateAnchorPosition(anchorComponent) { From 0c13f0254aa473611f6448dbba0371c0f2889bf4 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Fri, 9 Jun 2023 11:31:21 -0400 Subject: [PATCH 09/14] DRY up code --- src/components/EmojiPicker/EmojiPicker.js | 6 +++--- src/libs/ComponentUtils/index.js | 17 +---------------- src/libs/ComponentUtils/index.native.js | 17 +---------------- src/libs/calculateAnchorPosition.js | 14 ++++++++++++++ 4 files changed, 19 insertions(+), 35 deletions(-) create mode 100644 src/libs/calculateAnchorPosition.js diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index 0a39afb4c0d7..58d32ad6a3fb 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -5,11 +5,11 @@ import EmojiPickerMenu from './EmojiPickerMenu'; import CONST from '../../CONST'; import styles from '../../styles/styles'; import PopoverWithMeasuredContent from '../PopoverWithMeasuredContent'; -import * as ComponentUtils from '../../libs/ComponentUtils'; import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; import withViewportOffsetTop, {viewportOffsetTopPropTypes} from '../withViewportOffsetTop'; import compose from '../../libs/compose'; import * as StyleUtils from '../../styles/StyleUtils'; +import calculateAnchorPosition from '../../libs/calculateAnchorPosition'; const DEFAULT_ANCHOR_ORIGIN = { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, @@ -39,7 +39,7 @@ const EmojiPicker = forwardRef((props, ref) => { } const emojiPopoverDimensionListener = Dimensions.addEventListener('change', () => { - ComponentUtils.calculateAnchorPosition(emojiPopoverAnchor.current).then((value) => { + calculateAnchorPosition(emojiPopoverAnchor.current).then((value) => { setEmojiPopoverAnchorPosition(value); }); }); @@ -67,7 +67,7 @@ const EmojiPicker = forwardRef((props, ref) => { emojiPopoverAnchor.current.blur(); } - ComponentUtils.calculateAnchorPosition(emojiPopoverAnchor.current).then((value) => { + calculateAnchorPosition(emojiPopoverAnchor.current).then((value) => { onWillShow(); setIsEmojiPickerVisible(true); setEmojiPopoverAnchorPosition(value); diff --git a/src/libs/ComponentUtils/index.js b/src/libs/ComponentUtils/index.js index 8fc484f1768e..319926911c67 100644 --- a/src/libs/ComponentUtils/index.js +++ b/src/libs/ComponentUtils/index.js @@ -5,19 +5,4 @@ const PASSWORD_AUTOCOMPLETE_TYPE = 'current-password'; const NEW_PASSWORD_AUTOCOMPLETE_TYPE = 'new-password'; const ACCESSIBILITY_ROLE_FORM = 'form'; -/** - * Gets the x,y position of the passed in component for the purpose of anchoring another component to it. - * - * @param {Element} anchorComponent - * @return {Promise} - */ -function calculateAnchorPosition(anchorComponent) { - return new Promise((resolve) => { - if (!anchorComponent) { - return resolve({horizontal: 0, vertical: 0}); - } - anchorComponent.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y})); - }); -} - -export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE, calculateAnchorPosition}; +export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE}; diff --git a/src/libs/ComponentUtils/index.native.js b/src/libs/ComponentUtils/index.native.js index 20a511bf2f51..ed38345cbe2b 100644 --- a/src/libs/ComponentUtils/index.native.js +++ b/src/libs/ComponentUtils/index.native.js @@ -2,19 +2,4 @@ const PASSWORD_AUTOCOMPLETE_TYPE = 'password'; const NEW_PASSWORD_AUTOCOMPLETE_TYPE = 'password-new'; const ACCESSIBILITY_ROLE_FORM = 'none'; -/** - * Gets the x,y position of the passed in component for the purpose of anchoring another component to it. - * - * @param {Element} anchorComponent - * @return {Promise} - */ -function calculateAnchorPosition(anchorComponent) { - return new Promise((resolve) => { - if (!anchorComponent) { - return resolve({horizontal: 0, vertical: 0}); - } - anchorComponent.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y})); - }); -} - -export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE, calculateAnchorPosition}; +export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE}; diff --git a/src/libs/calculateAnchorPosition.js b/src/libs/calculateAnchorPosition.js new file mode 100644 index 000000000000..512f77612f52 --- /dev/null +++ b/src/libs/calculateAnchorPosition.js @@ -0,0 +1,14 @@ +/** + * Gets the x,y position of the passed in component for the purpose of anchoring another component to it. + * + * @param {Element} anchorComponent + * @return {Promise} + */ +export default function calculateAnchorPosition(anchorComponent) { + return new Promise((resolve) => { + if (!anchorComponent) { + return resolve({horizontal: 0, vertical: 0}); + } + anchorComponent.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y})); + }); +} From d4de81485a63997743c871a9d94f58d27173f54d Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Tue, 13 Jun 2023 21:52:35 -0700 Subject: [PATCH 10/14] Use function keyword --- src/components/EmojiPicker/EmojiPicker.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index 58d32ad6a3fb..fc99cd8205f4 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -21,7 +21,7 @@ const propTypes = { ...viewportOffsetTopPropTypes, }; -const EmojiPicker = forwardRef((props, ref) => { +function EmojiPicker(props, ref) { const [isEmojiPickerVisible, setIsEmojiPickerVisible] = useState(false); const [emojiPopoverAnchorPosition, setEmojiPopoverAnchorPosition] = useState({ horizontal: 0, @@ -152,7 +152,8 @@ const EmojiPicker = forwardRef((props, ref) => { /> ); -}); +} EmojiPicker.propTypes = propTypes; -export default compose(withViewportOffsetTop, withWindowDimensions)(EmojiPicker); +EmojiPicker.displayName = 'EmojiPicker'; +export default compose(withViewportOffsetTop, withWindowDimensions)(forwardRef(EmojiPicker)); From b9efb662d3386a2fd4a654dd8e65e787f7b0b559 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Tue, 20 Jun 2023 10:02:57 -0700 Subject: [PATCH 11/14] Expose hideEmojiPicker as well --- src/components/EmojiPicker/EmojiPicker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index 96bf7c64d849..378fbae1c695 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -130,7 +130,7 @@ function EmojiPicker(props, ref) { */ const isActiveReportAction = (actionID) => Boolean(actionID) && reportAction.reportActionID === actionID; - useImperativeHandle(ref, () => ({showEmojiPicker, isActiveReportAction})); + useImperativeHandle(ref, () => ({showEmojiPicker, isActiveReportAction, hideEmojiPicker})); // There is no way to disable animations, and they are really laggy, because there are so many // emojis. The best alternative is to set it to 1ms so it just "pops" in and out From 762a871f80b57a669b3666795e988f6e254b2ccb Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Tue, 20 Jun 2023 21:30:14 -0700 Subject: [PATCH 12/14] Apply forwardRef before attaching displayName and defaultProps --- src/components/EmojiPicker/EmojiPicker.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index 378fbae1c695..35a96eb38477 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -21,7 +21,7 @@ const propTypes = { ...viewportOffsetTopPropTypes, }; -function EmojiPicker(props, ref) { +const EmojiPicker = forwardRef((props, ref) => { const [isEmojiPickerVisible, setIsEmojiPickerVisible] = useState(false); const [emojiPopoverAnchorPosition, setEmojiPopoverAnchorPosition] = useState({ horizontal: 0, @@ -163,8 +163,8 @@ function EmojiPicker(props, ref) { /> ); -} +}); EmojiPicker.propTypes = propTypes; EmojiPicker.displayName = 'EmojiPicker'; -export default compose(withViewportOffsetTop, withWindowDimensions)(forwardRef(EmojiPicker)); +export default compose(withViewportOffsetTop, withWindowDimensions)(EmojiPicker); From 4cfa95389282973d691ead72d353b58294f40e5e Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Wed, 21 Jun 2023 10:06:28 -0700 Subject: [PATCH 13/14] Fix warning for forwardedRef type --- src/components/withViewportOffsetTop.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/withViewportOffsetTop.js b/src/components/withViewportOffsetTop.js index e25e5db9c5fa..0a8ed24cade2 100644 --- a/src/components/withViewportOffsetTop.js +++ b/src/components/withViewportOffsetTop.js @@ -53,7 +53,9 @@ export default function (WrappedComponent) { WithViewportOffsetTop.displayName = `WithViewportOffsetTop(${getComponentDisplayName(WrappedComponent)})`; WithViewportOffsetTop.propTypes = { - forwardedRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({current: PropTypes.instanceOf(React.Component)})]), + // eslint-disable-next-line react/forbid-prop-types + forwardedRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({current: PropTypes.any})]), + }; WithViewportOffsetTop.defaultProps = { forwardedRef: undefined, From a4a3e09356340faec10a5236b07645eed8fdbc40 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Wed, 21 Jun 2023 10:19:28 -0700 Subject: [PATCH 14/14] prettier --- src/components/withViewportOffsetTop.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/withViewportOffsetTop.js b/src/components/withViewportOffsetTop.js index 0a8ed24cade2..538071a948fa 100644 --- a/src/components/withViewportOffsetTop.js +++ b/src/components/withViewportOffsetTop.js @@ -55,7 +55,6 @@ export default function (WrappedComponent) { WithViewportOffsetTop.propTypes = { // eslint-disable-next-line react/forbid-prop-types forwardedRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({current: PropTypes.any})]), - }; WithViewportOffsetTop.defaultProps = { forwardedRef: undefined,