From 08e20a9796d426ff16e46caed4ab262800b37774 Mon Sep 17 00:00:00 2001 From: Maciej Dobosz Date: Mon, 28 Aug 2023 15:00:23 +0200 Subject: [PATCH 1/8] Rewrite to functional component --- src/components/withLocalize.js | 68 +++++++++++++--------------------- 1 file changed, 26 insertions(+), 42 deletions(-) diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js index def7110c1b40..4064ace042c0 100755 --- a/src/components/withLocalize.js +++ b/src/components/withLocalize.js @@ -62,49 +62,28 @@ const localeProviderDefaultProps = { currentUserPersonalDetails: {}, }; -class LocaleContextProvider extends React.Component { - /** - * The context this component exposes to child components - * @returns {object} translation util functions and locale - */ - getContextValue() { - return { - translate: this.translate.bind(this), - numberFormat: this.numberFormat.bind(this), - datetimeToRelative: this.datetimeToRelative.bind(this), - datetimeToCalendarTime: this.datetimeToCalendarTime.bind(this), - formatPhoneNumber: this.formatPhoneNumber.bind(this), - fromLocaleDigit: this.fromLocaleDigit.bind(this), - toLocaleDigit: this.toLocaleDigit.bind(this), - preferredLocale: this.props.preferredLocale, - }; - } +function LocaleContextProvider ({children, currentUserPersonalDetails, preferredLocale}) { + const selectedTimezone = lodashGet(currentUserPersonalDetails, 'timezone.selected'); /** * @param {String} phrase * @param {Object} [variables] * @returns {String} */ - translate(phrase, variables) { - return Localize.translate(this.props.preferredLocale, phrase, variables); - } + const translate = (phrase, variables) => Localize.translate(preferredLocale, phrase, variables) /** * @param {Number} number * @param {Intl.NumberFormatOptions} options * @returns {String} */ - numberFormat(number, options) { - return NumberFormatUtils.format(this.props.preferredLocale, number, options); - } + const numberFormat = (number, options) => NumberFormatUtils.format(preferredLocale, number, options) /** * @param {String} datetime * @returns {String} */ - datetimeToRelative(datetime) { - return DateUtils.datetimeToRelative(this.props.preferredLocale, datetime); - } + const datetimeToRelative = (datetime) => DateUtils.datetimeToRelative(preferredLocale, datetime) /** * @param {String} datetime - ISO-formatted datetime string @@ -112,37 +91,42 @@ class LocaleContextProvider extends React.Component { * @param {Boolean} isLowercase * @returns {String} */ - datetimeToCalendarTime(datetime, includeTimezone, isLowercase = false) { - return DateUtils.datetimeToCalendarTime(this.props.preferredLocale, datetime, includeTimezone, lodashGet(this.props, 'currentUserPersonalDetails.timezone.selected'), isLowercase); - } + const datetimeToCalendarTime = (datetime, includeTimezone, isLowercase = false) => DateUtils.datetimeToCalendarTime(preferredLocale, datetime, includeTimezone, selectedTimezone, isLowercase) /** * @param {String} phoneNumber * @returns {String} */ - formatPhoneNumber(phoneNumber) { - return LocalePhoneNumber.formatPhoneNumber(phoneNumber); - } + const formatPhoneNumber = (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber) /** * @param {String} digit * @returns {String} */ - toLocaleDigit(digit) { - return LocaleDigitUtils.toLocaleDigit(this.props.preferredLocale, digit); - } - + const toLocaleDigit = (digit) => LocaleDigitUtils.toLocaleDigit(preferredLocale, digit) + /** * @param {String} localeDigit * @returns {String} */ - fromLocaleDigit(localeDigit) { - return LocaleDigitUtils.fromLocaleDigit(this.props.preferredLocale, localeDigit); - } + const fromLocaleDigit = (localeDigit) => LocaleDigitUtils.fromLocaleDigit(preferredLocale, localeDigit) - render() { - return {this.props.children}; - } + /** + * The context this component exposes to child components + * @returns {object} translation util functions and locale + */ + const getContextValue = () => ({ + translate, + numberFormat, + datetimeToRelative, + datetimeToCalendarTime, + formatPhoneNumber, + toLocaleDigit, + fromLocaleDigit, + preferredLocale, + }) + + return {children}; } LocaleContextProvider.propTypes = localeProviderPropTypes; From bde2831bbd84eb7d175832281d018ec7305308f2 Mon Sep 17 00:00:00 2001 From: Maciej Dobosz Date: Tue, 29 Aug 2023 12:02:35 +0200 Subject: [PATCH 2/8] Run prettier --- src/components/withLocalize.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js index 4064ace042c0..f133980f96d5 100755 --- a/src/components/withLocalize.js +++ b/src/components/withLocalize.js @@ -62,7 +62,7 @@ const localeProviderDefaultProps = { currentUserPersonalDetails: {}, }; -function LocaleContextProvider ({children, currentUserPersonalDetails, preferredLocale}) { +function LocaleContextProvider({children, currentUserPersonalDetails, preferredLocale}) { const selectedTimezone = lodashGet(currentUserPersonalDetails, 'timezone.selected'); /** @@ -70,20 +70,20 @@ function LocaleContextProvider ({children, currentUserPersonalDetails, preferred * @param {Object} [variables] * @returns {String} */ - const translate = (phrase, variables) => Localize.translate(preferredLocale, phrase, variables) + const translate = (phrase, variables) => Localize.translate(preferredLocale, phrase, variables); /** * @param {Number} number * @param {Intl.NumberFormatOptions} options * @returns {String} */ - const numberFormat = (number, options) => NumberFormatUtils.format(preferredLocale, number, options) + const numberFormat = (number, options) => NumberFormatUtils.format(preferredLocale, number, options); /** * @param {String} datetime * @returns {String} */ - const datetimeToRelative = (datetime) => DateUtils.datetimeToRelative(preferredLocale, datetime) + const datetimeToRelative = (datetime) => DateUtils.datetimeToRelative(preferredLocale, datetime); /** * @param {String} datetime - ISO-formatted datetime string @@ -91,25 +91,26 @@ function LocaleContextProvider ({children, currentUserPersonalDetails, preferred * @param {Boolean} isLowercase * @returns {String} */ - const datetimeToCalendarTime = (datetime, includeTimezone, isLowercase = false) => DateUtils.datetimeToCalendarTime(preferredLocale, datetime, includeTimezone, selectedTimezone, isLowercase) + const datetimeToCalendarTime = (datetime, includeTimezone, isLowercase = false) => + DateUtils.datetimeToCalendarTime(preferredLocale, datetime, includeTimezone, selectedTimezone, isLowercase); /** * @param {String} phoneNumber * @returns {String} */ - const formatPhoneNumber = (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber) + const formatPhoneNumber = (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber); /** * @param {String} digit * @returns {String} */ - const toLocaleDigit = (digit) => LocaleDigitUtils.toLocaleDigit(preferredLocale, digit) - + const toLocaleDigit = (digit) => LocaleDigitUtils.toLocaleDigit(preferredLocale, digit); + /** * @param {String} localeDigit * @returns {String} */ - const fromLocaleDigit = (localeDigit) => LocaleDigitUtils.fromLocaleDigit(preferredLocale, localeDigit) + const fromLocaleDigit = (localeDigit) => LocaleDigitUtils.fromLocaleDigit(preferredLocale, localeDigit); /** * The context this component exposes to child components @@ -124,7 +125,7 @@ function LocaleContextProvider ({children, currentUserPersonalDetails, preferred toLocaleDigit, fromLocaleDigit, preferredLocale, - }) + }); return {children}; } From 0d9a79a4f8a141e7e8dd9af5be690e45d2db908c Mon Sep 17 00:00:00 2001 From: Maciej Dobosz Date: Wed, 6 Sep 2023 16:34:24 +0200 Subject: [PATCH 3/8] Extract LocaleContext to separate file --- .storybook/preview.js | 2 +- src/App.js | 2 +- src/components/createLocaleContext.js | 122 +++++++++++++++++++++++++ src/components/withLocalize.js | 123 +------------------------- src/hooks/useLocalize.js | 2 +- tests/utils/LHNTestUtils.js | 2 +- 6 files changed, 129 insertions(+), 124 deletions(-) create mode 100644 src/components/createLocaleContext.js diff --git a/.storybook/preview.js b/.storybook/preview.js index 7ccfd74e0e45..a13e93dbe8ad 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -6,7 +6,7 @@ import './fonts.css'; import ComposeProviders from '../src/components/ComposeProviders'; import HTMLEngineProvider from '../src/components/HTMLEngineProvider'; import OnyxProvider from '../src/components/OnyxProvider'; -import {LocaleContextProvider} from '../src/components/withLocalize'; +import {LocaleContextProvider} from '../src/components/createLocaleContext'; import ONYXKEYS from '../src/ONYXKEYS'; Onyx.init({ diff --git a/src/App.js b/src/App.js index c432a0b666c8..cd2e57a8d193 100644 --- a/src/App.js +++ b/src/App.js @@ -9,7 +9,7 @@ import {PickerStateProvider} from 'react-native-picker-select'; import CustomStatusBar from './components/CustomStatusBar'; import ErrorBoundary from './components/ErrorBoundary'; import Expensify from './Expensify'; -import {LocaleContextProvider} from './components/withLocalize'; +import {LocaleContextProvider} from './components/createLocaleContext'; import OnyxProvider from './components/OnyxProvider'; import HTMLEngineProvider from './components/HTMLEngineProvider'; import PopoverContextProvider from './components/PopoverProvider'; diff --git a/src/components/createLocaleContext.js b/src/components/createLocaleContext.js new file mode 100644 index 000000000000..be5bb9904b6a --- /dev/null +++ b/src/components/createLocaleContext.js @@ -0,0 +1,122 @@ +import React, {createContext} from 'react'; +import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; + +import ONYXKEYS from '../ONYXKEYS'; +import * as Localize from '../libs/Localize'; +import DateUtils from '../libs/DateUtils'; +import * as NumberFormatUtils from '../libs/NumberFormatUtils'; +import * as LocaleDigitUtils from '../libs/LocaleDigitUtils'; +import CONST from '../CONST'; +import compose from '../libs/compose'; +import withCurrentUserPersonalDetails from './withCurrentUserPersonalDetails'; +import * as LocalePhoneNumber from '../libs/LocalePhoneNumber'; + +const LocaleContext = createContext(null); + +const localeProviderPropTypes = { + /** The user's preferred locale e.g. 'en', 'es-ES' */ + preferredLocale: PropTypes.string, + + /** Actual content wrapped by this component */ + children: PropTypes.node.isRequired, + + /** The current user's personalDetails */ + currentUserPersonalDetails: PropTypes.shape({ + /** Timezone of the current user */ + timezone: PropTypes.shape({ + /** Value of the selected timezone */ + selected: PropTypes.string, + }), + }), +}; + +const localeProviderDefaultProps = { + preferredLocale: CONST.LOCALES.DEFAULT, + currentUserPersonalDetails: {}, +}; + +function LocaleContextProvider({children, currentUserPersonalDetails, preferredLocale}) { + const selectedTimezone = lodashGet(currentUserPersonalDetails, 'timezone.selected'); + + /** + * @param {String} phrase + * @param {Object} [variables] + * @returns {String} + */ + const translate = (phrase, variables) => Localize.translate(preferredLocale, phrase, variables); + + /** + * @param {Number} number + * @param {Intl.NumberFormatOptions} options + * @returns {String} + */ + const numberFormat = (number, options) => NumberFormatUtils.format(preferredLocale, number, options); + + /** + * @param {String} datetime + * @returns {String} + */ + const datetimeToRelative = (datetime) => DateUtils.datetimeToRelative(preferredLocale, datetime); + + /** + * @param {String} datetime - ISO-formatted datetime string + * @param {Boolean} [includeTimezone] + * @param {Boolean} isLowercase + * @returns {String} + */ + const datetimeToCalendarTime = (datetime, includeTimezone, isLowercase = false) => + DateUtils.datetimeToCalendarTime(preferredLocale, datetime, includeTimezone, selectedTimezone, isLowercase); + + /** + * @param {String} phoneNumber + * @returns {String} + */ + const formatPhoneNumber = (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber); + + /** + * @param {String} digit + * @returns {String} + */ + const toLocaleDigit = (digit) => LocaleDigitUtils.toLocaleDigit(preferredLocale, digit); + + /** + * @param {String} localeDigit + * @returns {String} + */ + const fromLocaleDigit = (localeDigit) => LocaleDigitUtils.fromLocaleDigit(preferredLocale, localeDigit); + + /** + * The context this component exposes to child components + * @returns {object} translation util functions and locale + */ + const getContextValue = () => ({ + translate, + numberFormat, + datetimeToRelative, + datetimeToCalendarTime, + formatPhoneNumber, + toLocaleDigit, + fromLocaleDigit, + preferredLocale, + }); + + return {children}; +} + +LocaleContextProvider.propTypes = localeProviderPropTypes; +LocaleContextProvider.defaultProps = localeProviderDefaultProps; + +const Provider = compose( + withCurrentUserPersonalDetails, + withOnyx({ + preferredLocale: { + key: ONYXKEYS.NVP_PREFERRED_LOCALE, + }, + }), +)(LocaleContextProvider); + +Provider.displayName = 'withOnyx(LocaleContextProvider)'; + +export {Provider as LocaleContextProvider, LocaleContext}; diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js index f133980f96d5..f717b822f6ee 100755 --- a/src/components/withLocalize.js +++ b/src/components/withLocalize.js @@ -1,20 +1,7 @@ -import React, {createContext, forwardRef} from 'react'; +import React, {forwardRef} from 'react'; import PropTypes from 'prop-types'; -import {withOnyx} from 'react-native-onyx'; -import lodashGet from 'lodash/get'; - +import {LocaleContext} from './createLocaleContext'; import getComponentDisplayName from '../libs/getComponentDisplayName'; -import ONYXKEYS from '../ONYXKEYS'; -import * as Localize from '../libs/Localize'; -import DateUtils from '../libs/DateUtils'; -import * as NumberFormatUtils from '../libs/NumberFormatUtils'; -import * as LocaleDigitUtils from '../libs/LocaleDigitUtils'; -import CONST from '../CONST'; -import compose from '../libs/compose'; -import withCurrentUserPersonalDetails from './withCurrentUserPersonalDetails'; -import * as LocalePhoneNumber from '../libs/LocalePhoneNumber'; - -const LocaleContext = createContext(null); const withLocalizePropTypes = { /** Returns translated string for given locale and phrase */ @@ -40,110 +27,6 @@ const withLocalizePropTypes = { toLocaleDigit: PropTypes.func.isRequired, }; -const localeProviderPropTypes = { - /** The user's preferred locale e.g. 'en', 'es-ES' */ - preferredLocale: PropTypes.string, - - /** Actual content wrapped by this component */ - children: PropTypes.node.isRequired, - - /** The current user's personalDetails */ - currentUserPersonalDetails: PropTypes.shape({ - /** Timezone of the current user */ - timezone: PropTypes.shape({ - /** Value of the selected timezone */ - selected: PropTypes.string, - }), - }), -}; - -const localeProviderDefaultProps = { - preferredLocale: CONST.LOCALES.DEFAULT, - currentUserPersonalDetails: {}, -}; - -function LocaleContextProvider({children, currentUserPersonalDetails, preferredLocale}) { - const selectedTimezone = lodashGet(currentUserPersonalDetails, 'timezone.selected'); - - /** - * @param {String} phrase - * @param {Object} [variables] - * @returns {String} - */ - const translate = (phrase, variables) => Localize.translate(preferredLocale, phrase, variables); - - /** - * @param {Number} number - * @param {Intl.NumberFormatOptions} options - * @returns {String} - */ - const numberFormat = (number, options) => NumberFormatUtils.format(preferredLocale, number, options); - - /** - * @param {String} datetime - * @returns {String} - */ - const datetimeToRelative = (datetime) => DateUtils.datetimeToRelative(preferredLocale, datetime); - - /** - * @param {String} datetime - ISO-formatted datetime string - * @param {Boolean} [includeTimezone] - * @param {Boolean} isLowercase - * @returns {String} - */ - const datetimeToCalendarTime = (datetime, includeTimezone, isLowercase = false) => - DateUtils.datetimeToCalendarTime(preferredLocale, datetime, includeTimezone, selectedTimezone, isLowercase); - - /** - * @param {String} phoneNumber - * @returns {String} - */ - const formatPhoneNumber = (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber); - - /** - * @param {String} digit - * @returns {String} - */ - const toLocaleDigit = (digit) => LocaleDigitUtils.toLocaleDigit(preferredLocale, digit); - - /** - * @param {String} localeDigit - * @returns {String} - */ - const fromLocaleDigit = (localeDigit) => LocaleDigitUtils.fromLocaleDigit(preferredLocale, localeDigit); - - /** - * The context this component exposes to child components - * @returns {object} translation util functions and locale - */ - const getContextValue = () => ({ - translate, - numberFormat, - datetimeToRelative, - datetimeToCalendarTime, - formatPhoneNumber, - toLocaleDigit, - fromLocaleDigit, - preferredLocale, - }); - - return {children}; -} - -LocaleContextProvider.propTypes = localeProviderPropTypes; -LocaleContextProvider.defaultProps = localeProviderDefaultProps; - -const Provider = compose( - withCurrentUserPersonalDetails, - withOnyx({ - preferredLocale: { - key: ONYXKEYS.NVP_PREFERRED_LOCALE, - }, - }), -)(LocaleContextProvider); - -Provider.displayName = 'withOnyx(LocaleContextProvider)'; - export default function withLocalize(WrappedComponent) { const WithLocalize = forwardRef((props, ref) => ( @@ -164,4 +47,4 @@ export default function withLocalize(WrappedComponent) { return WithLocalize; } -export {withLocalizePropTypes, Provider as LocaleContextProvider, LocaleContext}; +export {withLocalizePropTypes} \ No newline at end of file diff --git a/src/hooks/useLocalize.js b/src/hooks/useLocalize.js index 9ad5048729bd..f0a18758e0e9 100644 --- a/src/hooks/useLocalize.js +++ b/src/hooks/useLocalize.js @@ -1,5 +1,5 @@ import {useContext} from 'react'; -import {LocaleContext} from '../components/withLocalize'; +import {LocaleContext} from '../components/createLocaleContext'; export default function useLocalize() { return useContext(LocaleContext); diff --git a/tests/utils/LHNTestUtils.js b/tests/utils/LHNTestUtils.js index 43090cf024e2..e38e6d50d519 100644 --- a/tests/utils/LHNTestUtils.js +++ b/tests/utils/LHNTestUtils.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import {render} from '@testing-library/react-native'; import ComposeProviders from '../../src/components/ComposeProviders'; import OnyxProvider from '../../src/components/OnyxProvider'; -import {LocaleContextProvider} from '../../src/components/withLocalize'; +import {LocaleContextProvider} from '../../src/components/createLocaleContext'; import SidebarLinksData from '../../src/pages/home/sidebar/SidebarLinksData'; import {EnvironmentProvider} from '../../src/components/withEnvironment'; import CONST from '../../src/CONST'; From a4f1f064767020732d1b8ea57f9159f073c7c92b Mon Sep 17 00:00:00 2001 From: Maciej Dobosz Date: Wed, 6 Sep 2023 16:40:42 +0200 Subject: [PATCH 4/8] Missing semicolon --- src/components/withLocalize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js index f717b822f6ee..4687a2f9fe7f 100755 --- a/src/components/withLocalize.js +++ b/src/components/withLocalize.js @@ -47,4 +47,4 @@ export default function withLocalize(WrappedComponent) { return WithLocalize; } -export {withLocalizePropTypes} \ No newline at end of file +export {withLocalizePropTypes}; From a994d7686c1f67e7010d36f787cbf59c59406ab9 Mon Sep 17 00:00:00 2001 From: Wojciech Lewicki Date: Wed, 20 Sep 2023 14:54:22 +0200 Subject: [PATCH 5/8] feat: add React memo --- .storybook/preview.js | 2 +- src/App.js | 2 +- src/components/LocaleContextProvider.js | 127 ++++++++++++++++++++++++ src/components/createLocaleContext.js | 122 ----------------------- src/components/withLocalize.js | 2 +- src/hooks/useLocalize.js | 2 +- tests/utils/LHNTestUtils.js | 2 +- 7 files changed, 132 insertions(+), 127 deletions(-) create mode 100644 src/components/LocaleContextProvider.js delete mode 100644 src/components/createLocaleContext.js diff --git a/.storybook/preview.js b/.storybook/preview.js index 37e2e95bce4e..a989960794f2 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -6,7 +6,7 @@ import './fonts.css'; import ComposeProviders from '../src/components/ComposeProviders'; import HTMLEngineProvider from '../src/components/HTMLEngineProvider'; import OnyxProvider from '../src/components/OnyxProvider'; -import {LocaleContextProvider} from '../src/components/createLocaleContext'; +import {LocaleContextProvider} from '../src/components/LocaleContextProvider'; import {KeyboardStateProvider} from '../src/components/withKeyboardState'; import {EnvironmentProvider} from '../src/components/withEnvironment'; import {WindowDimensionsProvider} from '../src/components/withWindowDimensions'; diff --git a/src/App.js b/src/App.js index 301c518b9490..1d2e07345c24 100644 --- a/src/App.js +++ b/src/App.js @@ -9,7 +9,7 @@ import {PickerStateProvider} from 'react-native-picker-select'; import CustomStatusBar from './components/CustomStatusBar'; import ErrorBoundary from './components/ErrorBoundary'; import Expensify from './Expensify'; -import {LocaleContextProvider} from './components/createLocaleContext'; +import {LocaleContextProvider} from './components/LocaleContextProvider'; import OnyxProvider from './components/OnyxProvider'; import HTMLEngineProvider from './components/HTMLEngineProvider'; import PopoverContextProvider from './components/PopoverProvider'; diff --git a/src/components/LocaleContextProvider.js b/src/components/LocaleContextProvider.js new file mode 100644 index 000000000000..fc6dca4a092e --- /dev/null +++ b/src/components/LocaleContextProvider.js @@ -0,0 +1,127 @@ +import React, {createContext} from 'react'; +import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; + +import ONYXKEYS from '../ONYXKEYS'; +import * as Localize from '../libs/Localize'; +import DateUtils from '../libs/DateUtils'; +import * as NumberFormatUtils from '../libs/NumberFormatUtils'; +import * as LocaleDigitUtils from '../libs/LocaleDigitUtils'; +import CONST from '../CONST'; +import compose from '../libs/compose'; +import withCurrentUserPersonalDetails from './withCurrentUserPersonalDetails'; +import * as LocalePhoneNumber from '../libs/LocalePhoneNumber'; + +const LocaleContext = createContext(null); + +const localeProviderPropTypes = { + /** The user's preferred locale e.g. 'en', 'es-ES' */ + preferredLocale: PropTypes.string, + + /** Actual content wrapped by this component */ + children: PropTypes.node.isRequired, + + /** The current user's personalDetails */ + currentUserPersonalDetails: PropTypes.shape({ + /** Timezone of the current user */ + timezone: PropTypes.shape({ + /** Value of the selected timezone */ + selected: PropTypes.string, + }), + }), +}; + +const localeProviderDefaultProps = { + preferredLocale: CONST.LOCALES.DEFAULT, + currentUserPersonalDetails: {}, +}; + +const LocaleContextProvider = React.memo( + ({children, currentUserPersonalDetails, preferredLocale}) => { + const selectedTimezone = lodashGet(currentUserPersonalDetails, 'timezone.selected'); + + /** + * @param {String} phrase + * @param {Object} [variables] + * @returns {String} + */ + const translate = (phrase, variables) => Localize.translate(preferredLocale, phrase, variables); + + /** + * @param {Number} number + * @param {Intl.NumberFormatOptions} options + * @returns {String} + */ + const numberFormat = (number, options) => NumberFormatUtils.format(preferredLocale, number, options); + + /** + * @param {String} datetime + * @returns {String} + */ + const datetimeToRelative = (datetime) => DateUtils.datetimeToRelative(preferredLocale, datetime); + + /** + * @param {String} datetime - ISO-formatted datetime string + * @param {Boolean} [includeTimezone] + * @param {Boolean} isLowercase + * @returns {String} + */ + const datetimeToCalendarTime = (datetime, includeTimezone, isLowercase = false) => + DateUtils.datetimeToCalendarTime(preferredLocale, datetime, includeTimezone, selectedTimezone, isLowercase); + + /** + * @param {String} phoneNumber + * @returns {String} + */ + const formatPhoneNumber = (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber); + + /** + * @param {String} digit + * @returns {String} + */ + const toLocaleDigit = (digit) => LocaleDigitUtils.toLocaleDigit(preferredLocale, digit); + + /** + * @param {String} localeDigit + * @returns {String} + */ + const fromLocaleDigit = (localeDigit) => LocaleDigitUtils.fromLocaleDigit(preferredLocale, localeDigit); + + /** + * The context this component exposes to child components + * @returns {object} translation util functions and locale + */ + const getContextValue = () => ({ + translate, + numberFormat, + datetimeToRelative, + datetimeToCalendarTime, + formatPhoneNumber, + toLocaleDigit, + fromLocaleDigit, + preferredLocale, + }); + + return {children}; + }, + (prevProps, nextProps) => + nextProps.preferredLocale === prevProps.preferredLocale && + lodashGet(nextProps, 'currentUserPersonalDetails.timezone.selected') === lodashGet(prevProps, 'currentUserPersonalDetails.timezone.selected'), +); + +LocaleContextProvider.propTypes = localeProviderPropTypes; +LocaleContextProvider.defaultProps = localeProviderDefaultProps; + +const Provider = compose( + withCurrentUserPersonalDetails, + withOnyx({ + preferredLocale: { + key: ONYXKEYS.NVP_PREFERRED_LOCALE, + }, + }), +)(LocaleContextProvider); + +Provider.displayName = 'withOnyx(LocaleContextProvider)'; + +export {Provider as LocaleContextProvider, LocaleContext}; diff --git a/src/components/createLocaleContext.js b/src/components/createLocaleContext.js deleted file mode 100644 index be5bb9904b6a..000000000000 --- a/src/components/createLocaleContext.js +++ /dev/null @@ -1,122 +0,0 @@ -import React, {createContext} from 'react'; -import PropTypes from 'prop-types'; -import {withOnyx} from 'react-native-onyx'; -import lodashGet from 'lodash/get'; - -import ONYXKEYS from '../ONYXKEYS'; -import * as Localize from '../libs/Localize'; -import DateUtils from '../libs/DateUtils'; -import * as NumberFormatUtils from '../libs/NumberFormatUtils'; -import * as LocaleDigitUtils from '../libs/LocaleDigitUtils'; -import CONST from '../CONST'; -import compose from '../libs/compose'; -import withCurrentUserPersonalDetails from './withCurrentUserPersonalDetails'; -import * as LocalePhoneNumber from '../libs/LocalePhoneNumber'; - -const LocaleContext = createContext(null); - -const localeProviderPropTypes = { - /** The user's preferred locale e.g. 'en', 'es-ES' */ - preferredLocale: PropTypes.string, - - /** Actual content wrapped by this component */ - children: PropTypes.node.isRequired, - - /** The current user's personalDetails */ - currentUserPersonalDetails: PropTypes.shape({ - /** Timezone of the current user */ - timezone: PropTypes.shape({ - /** Value of the selected timezone */ - selected: PropTypes.string, - }), - }), -}; - -const localeProviderDefaultProps = { - preferredLocale: CONST.LOCALES.DEFAULT, - currentUserPersonalDetails: {}, -}; - -function LocaleContextProvider({children, currentUserPersonalDetails, preferredLocale}) { - const selectedTimezone = lodashGet(currentUserPersonalDetails, 'timezone.selected'); - - /** - * @param {String} phrase - * @param {Object} [variables] - * @returns {String} - */ - const translate = (phrase, variables) => Localize.translate(preferredLocale, phrase, variables); - - /** - * @param {Number} number - * @param {Intl.NumberFormatOptions} options - * @returns {String} - */ - const numberFormat = (number, options) => NumberFormatUtils.format(preferredLocale, number, options); - - /** - * @param {String} datetime - * @returns {String} - */ - const datetimeToRelative = (datetime) => DateUtils.datetimeToRelative(preferredLocale, datetime); - - /** - * @param {String} datetime - ISO-formatted datetime string - * @param {Boolean} [includeTimezone] - * @param {Boolean} isLowercase - * @returns {String} - */ - const datetimeToCalendarTime = (datetime, includeTimezone, isLowercase = false) => - DateUtils.datetimeToCalendarTime(preferredLocale, datetime, includeTimezone, selectedTimezone, isLowercase); - - /** - * @param {String} phoneNumber - * @returns {String} - */ - const formatPhoneNumber = (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber); - - /** - * @param {String} digit - * @returns {String} - */ - const toLocaleDigit = (digit) => LocaleDigitUtils.toLocaleDigit(preferredLocale, digit); - - /** - * @param {String} localeDigit - * @returns {String} - */ - const fromLocaleDigit = (localeDigit) => LocaleDigitUtils.fromLocaleDigit(preferredLocale, localeDigit); - - /** - * The context this component exposes to child components - * @returns {object} translation util functions and locale - */ - const getContextValue = () => ({ - translate, - numberFormat, - datetimeToRelative, - datetimeToCalendarTime, - formatPhoneNumber, - toLocaleDigit, - fromLocaleDigit, - preferredLocale, - }); - - return {children}; -} - -LocaleContextProvider.propTypes = localeProviderPropTypes; -LocaleContextProvider.defaultProps = localeProviderDefaultProps; - -const Provider = compose( - withCurrentUserPersonalDetails, - withOnyx({ - preferredLocale: { - key: ONYXKEYS.NVP_PREFERRED_LOCALE, - }, - }), -)(LocaleContextProvider); - -Provider.displayName = 'withOnyx(LocaleContextProvider)'; - -export {Provider as LocaleContextProvider, LocaleContext}; diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js index 4687a2f9fe7f..9daa0ffcebf6 100755 --- a/src/components/withLocalize.js +++ b/src/components/withLocalize.js @@ -1,6 +1,6 @@ import React, {forwardRef} from 'react'; import PropTypes from 'prop-types'; -import {LocaleContext} from './createLocaleContext'; +import {LocaleContext} from './LocaleContextProvider'; import getComponentDisplayName from '../libs/getComponentDisplayName'; const withLocalizePropTypes = { diff --git a/src/hooks/useLocalize.js b/src/hooks/useLocalize.js index f0a18758e0e9..7f7a610fca8b 100644 --- a/src/hooks/useLocalize.js +++ b/src/hooks/useLocalize.js @@ -1,5 +1,5 @@ import {useContext} from 'react'; -import {LocaleContext} from '../components/createLocaleContext'; +import {LocaleContext} from '../components/LocaleContextProvider'; export default function useLocalize() { return useContext(LocaleContext); diff --git a/tests/utils/LHNTestUtils.js b/tests/utils/LHNTestUtils.js index e38e6d50d519..7cb69b23a578 100644 --- a/tests/utils/LHNTestUtils.js +++ b/tests/utils/LHNTestUtils.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import {render} from '@testing-library/react-native'; import ComposeProviders from '../../src/components/ComposeProviders'; import OnyxProvider from '../../src/components/OnyxProvider'; -import {LocaleContextProvider} from '../../src/components/createLocaleContext'; +import {LocaleContextProvider} from '../../src/components/LocaleContextProvider'; import SidebarLinksData from '../../src/pages/home/sidebar/SidebarLinksData'; import {EnvironmentProvider} from '../../src/components/withEnvironment'; import CONST from '../../src/CONST'; From 6d1f82ff77f539160aa5eb3f72637bd26a6b5f33 Mon Sep 17 00:00:00 2001 From: Wojciech Lewicki Date: Thu, 21 Sep 2023 13:51:07 +0200 Subject: [PATCH 6/8] fix: dont memo the whole function --- src/components/LocaleContextProvider.js | 132 ++++++++++++------------ 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/src/components/LocaleContextProvider.js b/src/components/LocaleContextProvider.js index fc6dca4a092e..767a23ba123a 100644 --- a/src/components/LocaleContextProvider.js +++ b/src/components/LocaleContextProvider.js @@ -1,4 +1,4 @@ -import React, {createContext} from 'react'; +import React, {createContext, useMemo} from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; @@ -37,62 +37,66 @@ const localeProviderDefaultProps = { currentUserPersonalDetails: {}, }; -const LocaleContextProvider = React.memo( - ({children, currentUserPersonalDetails, preferredLocale}) => { - const selectedTimezone = lodashGet(currentUserPersonalDetails, 'timezone.selected'); - - /** - * @param {String} phrase - * @param {Object} [variables] - * @returns {String} - */ - const translate = (phrase, variables) => Localize.translate(preferredLocale, phrase, variables); - - /** - * @param {Number} number - * @param {Intl.NumberFormatOptions} options - * @returns {String} - */ - const numberFormat = (number, options) => NumberFormatUtils.format(preferredLocale, number, options); - - /** - * @param {String} datetime - * @returns {String} - */ - const datetimeToRelative = (datetime) => DateUtils.datetimeToRelative(preferredLocale, datetime); - - /** - * @param {String} datetime - ISO-formatted datetime string - * @param {Boolean} [includeTimezone] - * @param {Boolean} isLowercase - * @returns {String} - */ - const datetimeToCalendarTime = (datetime, includeTimezone, isLowercase = false) => - DateUtils.datetimeToCalendarTime(preferredLocale, datetime, includeTimezone, selectedTimezone, isLowercase); - - /** - * @param {String} phoneNumber - * @returns {String} - */ - const formatPhoneNumber = (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber); - - /** - * @param {String} digit - * @returns {String} - */ - const toLocaleDigit = (digit) => LocaleDigitUtils.toLocaleDigit(preferredLocale, digit); - - /** - * @param {String} localeDigit - * @returns {String} - */ - const fromLocaleDigit = (localeDigit) => LocaleDigitUtils.fromLocaleDigit(preferredLocale, localeDigit); - - /** - * The context this component exposes to child components - * @returns {object} translation util functions and locale - */ - const getContextValue = () => ({ +function LocaleContextProvider({children, currentUserPersonalDetails, preferredLocale}) { + const selectedTimezone = useMemo(() => lodashGet(currentUserPersonalDetails, 'timezone.selected'), [currentUserPersonalDetails]); + + /** + * @param {String} phrase + * @param {Object} [variables] + * @returns {String} + */ + const translate = useMemo(() => (phrase, variables) => Localize.translate(preferredLocale, phrase, variables), [preferredLocale]); + + /** + * @param {Number} number + * @param {Intl.NumberFormatOptions} options + * @returns {String} + */ + const numberFormat = useMemo(() => (number, options) => NumberFormatUtils.format(preferredLocale, number, options), [preferredLocale]); + + /** + * @param {String} datetime + * @returns {String} + */ + const datetimeToRelative = useMemo(() => (datetime) => DateUtils.datetimeToRelative(preferredLocale, datetime), [preferredLocale]); + + /** + * @param {String} datetime - ISO-formatted datetime string + * @param {Boolean} [includeTimezone] + * @param {Boolean} isLowercase + * @returns {String} + */ + const datetimeToCalendarTime = useMemo( + () => + (datetime, includeTimezone, isLowercase = false) => + DateUtils.datetimeToCalendarTime(preferredLocale, datetime, includeTimezone, selectedTimezone, isLowercase), + [preferredLocale, selectedTimezone], + ); + + /** + * @param {String} phoneNumber + * @returns {String} + */ + const formatPhoneNumber = useMemo(() => (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber), []); + + /** + * @param {String} digit + * @returns {String} + */ + const toLocaleDigit = useMemo(() => (digit) => LocaleDigitUtils.toLocaleDigit(preferredLocale, digit), [preferredLocale]); + + /** + * @param {String} localeDigit + * @returns {String} + */ + const fromLocaleDigit = useMemo(() => (localeDigit) => LocaleDigitUtils.fromLocaleDigit(preferredLocale, localeDigit), [preferredLocale]); + + /** + * The context this component exposes to child components + * @returns {object} translation util functions and locale + */ + const contextValue = useMemo( + () => ({ translate, numberFormat, datetimeToRelative, @@ -101,14 +105,12 @@ const LocaleContextProvider = React.memo( toLocaleDigit, fromLocaleDigit, preferredLocale, - }); - - return {children}; - }, - (prevProps, nextProps) => - nextProps.preferredLocale === prevProps.preferredLocale && - lodashGet(nextProps, 'currentUserPersonalDetails.timezone.selected') === lodashGet(prevProps, 'currentUserPersonalDetails.timezone.selected'), -); + }), + [translate, numberFormat, datetimeToRelative, datetimeToCalendarTime, formatPhoneNumber, toLocaleDigit, fromLocaleDigit, preferredLocale], + ); + + return {children}; +} LocaleContextProvider.propTypes = localeProviderPropTypes; LocaleContextProvider.defaultProps = localeProviderDefaultProps; From b216223d8d38f75b2240eccec9e01bb5f8cd631c Mon Sep 17 00:00:00 2001 From: Wojciech Lewicki Date: Thu, 21 Sep 2023 15:05:30 +0200 Subject: [PATCH 7/8] fix: easier impl --- src/components/LocaleContextProvider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/LocaleContextProvider.js b/src/components/LocaleContextProvider.js index 767a23ba123a..5afd0f4e3c75 100644 --- a/src/components/LocaleContextProvider.js +++ b/src/components/LocaleContextProvider.js @@ -77,7 +77,7 @@ function LocaleContextProvider({children, currentUserPersonalDetails, preferredL * @param {String} phoneNumber * @returns {String} */ - const formatPhoneNumber = useMemo(() => (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber), []); + const formatPhoneNumber = LocalePhoneNumber.formatPhoneNumber; /** * @param {String} digit From f5b12d2c4c5010939a7e75c0c9eb3427db1d373b Mon Sep 17 00:00:00 2001 From: Maciej Dobosz Date: Thu, 28 Sep 2023 13:37:09 +0200 Subject: [PATCH 8/8] Return function instead of direct value --- src/components/LocaleContextProvider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/LocaleContextProvider.js b/src/components/LocaleContextProvider.js index f2525c5e3524..b8838f253e74 100644 --- a/src/components/LocaleContextProvider.js +++ b/src/components/LocaleContextProvider.js @@ -76,7 +76,7 @@ function LocaleContextProvider({children, currentUserPersonalDetails, preferredL /** * Updates date-fns internal locale to the user preferredLocale */ - const updateLocale = useMemo(() => DateUtils.setLocale(preferredLocale), [preferredLocale]); + const updateLocale = useMemo(() => () => DateUtils.setLocale(preferredLocale), [preferredLocale]); /** * @param {String} phoneNumber