diff --git a/src/App.js b/src/App.js
index 3fd37312645ee..ae1b8860f6062 100644
--- a/src/App.js
+++ b/src/App.js
@@ -5,6 +5,8 @@ import CustomStatusBar from './components/CustomStatusBar';
import ErrorBoundary from './components/ErrorBoundary';
import Expensify from './Expensify';
import {LocaleContextProvider} from './components/withLocalize';
+import OnyxProvider from './components/OnyxProvider';
+import ComposeProviders from './components/ComposeProviders';
LogBox.ignoreLogs([
// Basically it means that if the app goes in the background and back to foreground on Android,
@@ -17,14 +19,18 @@ LogBox.ignoreLogs([
]);
const App = () => (
-
-
-
-
-
-
-
-
+
+
+
+
+
+
);
App.displayName = 'App';
diff --git a/src/Expensify.js b/src/Expensify.js
index 53cef50574e63..60134552bde4b 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -2,16 +2,13 @@ import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React, {PureComponent} from 'react';
import {View, AppState} from 'react-native';
-import Onyx, {withOnyx} from 'react-native-onyx';
+import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import BootSplash from './libs/BootSplash';
-import listenToStorageEvents from './libs/listenToStorageEvents';
import * as ActiveClientManager from './libs/ActiveClientManager';
import ONYXKEYS from './ONYXKEYS';
-import CONST from './CONST';
import NavigationRoot from './libs/Navigation/NavigationRoot';
-import Log from './libs/Log';
import migrateOnyx from './libs/migrateOnyx';
import styles from './styles/styles';
import PushNotification from './libs/Notification/PushNotification';
@@ -24,33 +21,6 @@ import ROUTES from './ROUTES';
import StartupTimer from './libs/StartupTimer';
import {setRedirectToWorkspaceNewAfterSignIn} from './libs/actions/Session';
-// Initialize the store when the app loads for the first time
-Onyx.init({
- keys: ONYXKEYS,
- safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS],
- initialKeyStates: {
-
- // Clear any loading and error messages so they do not appear on app startup
- [ONYXKEYS.SESSION]: {loading: false, shouldShowComposeInput: true},
- [ONYXKEYS.ACCOUNT]: CONST.DEFAULT_ACCOUNT_DATA,
- [ONYXKEYS.NETWORK]: {isOffline: false},
- [ONYXKEYS.IOU]: {
- loading: false, error: false, creatingIOUTransaction: false, isRetrievingCurrency: false,
- },
- [ONYXKEYS.IS_SIDEBAR_LOADED]: false,
- },
- registerStorageEventListener: (onStorageEvent) => {
- listenToStorageEvents(onStorageEvent);
- },
-});
-Onyx.registerLogger(({level, message}) => {
- if (level === 'alert') {
- Log.alert(message, 0, {}, false);
- } else {
- Log.client(message);
- }
-});
-
const propTypes = {
/* Onyx Props */
diff --git a/src/components/ComposeProviders.js b/src/components/ComposeProviders.js
new file mode 100644
index 0000000000000..7cf9af7348d13
--- /dev/null
+++ b/src/components/ComposeProviders.js
@@ -0,0 +1,22 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const propTypes = {
+ /** Provider components go here */
+ components: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.object, PropTypes.func])).isRequired,
+
+ /** Rendered child component */
+ children: PropTypes.node.isRequired,
+};
+
+const ComposeProviders = props => (
+ <>
+ {props.components.reduceRight((memo, Component) => (
+ {memo}
+ ), props.children)}
+ >
+);
+
+ComposeProviders.propTypes = propTypes;
+ComposeProviders.displayName = 'ComposeProviders';
+export default ComposeProviders;
diff --git a/src/components/OnyxProvider.js b/src/components/OnyxProvider.js
new file mode 100644
index 0000000000000..f88a5e078e31a
--- /dev/null
+++ b/src/components/OnyxProvider.js
@@ -0,0 +1,73 @@
+import React from 'react';
+import Onyx from 'react-native-onyx';
+import PropTypes from 'prop-types';
+import ONYXKEYS from '../ONYXKEYS';
+import createOnyxContext from './createOnyxContext';
+import ComposeProviders from './ComposeProviders';
+import CONST from '../CONST';
+import Log from '../libs/Log';
+import listenToStorageEvents from '../libs/listenToStorageEvents';
+
+// Initialize the store when the app loads for the first time
+Onyx.init({
+ keys: ONYXKEYS,
+ safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS],
+ initialKeyStates: {
+
+ // Clear any loading and error messages so they do not appear on app startup
+ [ONYXKEYS.SESSION]: {loading: false, shouldShowComposeInput: true},
+ [ONYXKEYS.ACCOUNT]: CONST.DEFAULT_ACCOUNT_DATA,
+ [ONYXKEYS.NETWORK]: {isOffline: false},
+ [ONYXKEYS.IOU]: {
+ loading: false, error: false, creatingIOUTransaction: false, isRetrievingCurrency: false,
+ },
+ [ONYXKEYS.IS_SIDEBAR_LOADED]: false,
+ },
+ registerStorageEventListener: (onStorageEvent) => {
+ listenToStorageEvents(onStorageEvent);
+ },
+});
+Onyx.registerLogger(({level, message}) => {
+ if (level === 'alert') {
+ Log.alert(message, 0, {}, false);
+ } else {
+ Log.client(message);
+ }
+});
+
+// Set up any providers for individual keys. This should only be used in cases where many components will subscribe to
+// the same key (e.g. FlatList renderItem components)
+const [withNetwork, NetworkProvider] = createOnyxContext(ONYXKEYS.NETWORK);
+const [withPersonalDetails, PersonalDetailsProvider] = createOnyxContext(ONYXKEYS.PERSONAL_DETAILS);
+const [
+ withReportActionsDrafts,
+ ReportActionsDraftsProvider,
+] = createOnyxContext(ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS);
+
+const propTypes = {
+ /** Rendered child component */
+ children: PropTypes.node.isRequired,
+};
+
+const OnyxProvider = props => (
+
+ {props.children}
+
+);
+
+OnyxProvider.displayName = 'OnyxProvider';
+OnyxProvider.propTypes = propTypes;
+
+export default OnyxProvider;
+
+export {
+ withNetwork,
+ withPersonalDetails,
+ withReportActionsDrafts,
+};
diff --git a/src/components/createOnyxContext.js b/src/components/createOnyxContext.js
new file mode 100644
index 0000000000000..9304efd4a430b
--- /dev/null
+++ b/src/components/createOnyxContext.js
@@ -0,0 +1,50 @@
+import React, {createContext, forwardRef} from 'react';
+import PropTypes from 'prop-types';
+import {withOnyx} from 'react-native-onyx';
+import Str from 'expensify-common/lib/str';
+import getComponentDisplayName from '../libs/getComponentDisplayName';
+
+const propTypes = {
+ /** Rendered child component */
+ children: PropTypes.node.isRequired,
+};
+
+export default (onyxKeyName) => {
+ const Context = createContext();
+ const Provider = props => (
+
+ {props.children}
+
+ );
+
+ Provider.propTypes = propTypes;
+ Provider.displayName = `${Str.UCFirst(onyxKeyName)}Provider`;
+
+ const ProviderWithOnyx = withOnyx({
+ [onyxKeyName]: {
+ key: onyxKeyName,
+ },
+ })(Provider);
+
+ const withOnyxKey = ({propName = onyxKeyName, transformValue = () => {}} = {}) => (WrappedComponent) => {
+ const Consumer = forwardRef((props, ref) => (
+
+ {(value) => {
+ const propsToPass = {
+ ...props,
+ [propName]: transformValue ? transformValue(value, props) : value,
+ };
+ return (
+ // eslint-disable-next-line react/jsx-props-no-spreading
+
+ );
+ }}
+
+ ));
+
+ Consumer.displayName = `with${Str.UCFirst(onyxKeyName)}(${getComponentDisplayName(WrappedComponent)})`;
+ return Consumer;
+ };
+
+ return [withOnyxKey, ProviderWithOnyx];
+};
diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 010f9e2998172..641ad7904ebb7 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -1,8 +1,8 @@
import _ from 'underscore';
+import lodashGet from 'lodash/get';
import React, {Component} from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
-import {withOnyx} from 'react-native-onyx';
import CONST from '../../../CONST';
import ONYXKEYS from '../../../ONYXKEYS';
import ReportActionPropTypes from './ReportActionPropTypes';
@@ -23,6 +23,7 @@ import ControlSelection from '../../../libs/ControlSelection';
import canUseTouchScreen from '../../../libs/canUseTouchscreen';
import MiniReportActionContextMenu from './ContextMenu/MiniReportActionContextMenu';
import {isActiveReportAction, showContextMenu} from './ContextMenu/ReportActionContextMenu';
+import {withReportActionsDrafts} from '../../../components/OnyxProvider';
const propTypes = {
/** The ID of the report this action is on. */
@@ -185,12 +186,12 @@ ReportActionItem.defaultProps = defaultProps;
export default compose(
withWindowDimensions,
- withOnyx({
- draftMessage: {
- key: ({
- reportID,
- action,
- }) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}_${action.reportActionID}`,
+ withReportActionsDrafts({
+ propName: 'draftMessage',
+ transformValue: (drafts, props) => {
+ const {reportID, action} = props;
+ const draftKey = `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}_${action.reportActionID}`;
+ return lodashGet(drafts, draftKey, '');
},
}),
)(ReportActionItem);
diff --git a/src/pages/home/report/ReportActionItemMessage.js b/src/pages/home/report/ReportActionItemMessage.js
index 7f727f84e33a0..d57e63e0cdad7 100644
--- a/src/pages/home/report/ReportActionItemMessage.js
+++ b/src/pages/home/report/ReportActionItemMessage.js
@@ -2,11 +2,10 @@ import React from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import _ from 'underscore';
-import {withOnyx} from 'react-native-onyx';
-import ONYXKEYS from '../../../ONYXKEYS';
import styles from '../../../styles/styles';
import ReportActionItemFragment from './ReportActionItemFragment';
import ReportActionPropTypes from './ReportActionPropTypes';
+import {withNetwork} from '../../../components/OnyxProvider';
const propTypes = {
/** The report action */
@@ -43,8 +42,4 @@ ReportActionItemMessage.propTypes = propTypes;
ReportActionItemMessage.defaultProps = defaultProps;
ReportActionItemMessage.displayName = 'ReportActionItemMessage';
-export default withOnyx({
- network: {
- key: ONYXKEYS.NETWORK,
- },
-})(ReportActionItemMessage);
+export default withNetwork()(ReportActionItemMessage);
diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js
index 84befd28f4c8d..7f05458a4af6b 100644
--- a/src/pages/home/report/ReportActionItemSingle.js
+++ b/src/pages/home/report/ReportActionItemSingle.js
@@ -1,6 +1,5 @@
import React from 'react';
import {View, Pressable} from 'react-native';
-import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import _ from 'underscore';
import Str from 'expensify-common/lib/str';
@@ -10,12 +9,12 @@ import styles from '../../../styles/styles';
import CONST from '../../../CONST';
import ReportActionItemDate from './ReportActionItemDate';
import Avatar from '../../../components/Avatar';
-import ONYXKEYS from '../../../ONYXKEYS';
import personalDetailsPropType from '../../personalDetailsPropType';
import compose from '../../../libs/compose';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import Navigation from '../../../libs/Navigation/Navigation';
import ROUTES from '../../../ROUTES';
+import {withPersonalDetails} from '../../../components/OnyxProvider';
const propTypes = {
/** All the data of the action */
@@ -99,9 +98,5 @@ ReportActionItemSingle.displayName = 'ReportActionItemSingle';
export default compose(
withLocalize,
- withOnyx({
- personalDetails: {
- key: ONYXKEYS.PERSONAL_DETAILS,
- },
- }),
+ withPersonalDetails(),
)(ReportActionItemSingle);