-
Notifications
You must be signed in to change notification settings - Fork 314
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(rwa): remove user destructure in getServiceContextFacade
- Loading branch information
1 parent
4e2c13e
commit f0d762d
Showing
13 changed files
with
315 additions
and
1 deletion.
There are no files selected for viewing
8 changes: 8 additions & 0 deletions
8
packages/react-core/dist/Authenticator/hooks/useAuthenticator/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.useAuthenticator = void 0; | ||
var useAuthenticator_1 = require("./useAuthenticator"); | ||
Object.defineProperty(exports, "useAuthenticator", { enumerable: true, get: function () { return __importDefault(useAuthenticator_1).default; } }); |
120 changes: 120 additions & 0 deletions
120
packages/react-core/dist/Authenticator/hooks/useAuthenticator/useAuthenticator.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
"use strict"; | ||
// export default function useAuthenticator(): null { | ||
// return null; | ||
// } | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Provider = exports.AuthenticatorContext = exports.areArrayValuesEqual = void 0; | ||
const tslib_1 = require("tslib"); | ||
const react_1 = tslib_1.__importDefault(require("react")); | ||
const ui_1 = require("@aws-amplify/ui"); | ||
const react_2 = require("@xstate/react"); | ||
const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty")); | ||
const isArray_1 = tslib_1.__importDefault(require("lodash/isArray")); | ||
const isObject_1 = tslib_1.__importDefault(require("lodash/isObject")); | ||
function isEmptyObj(val) { | ||
return (0, isObject_1.default)(val) && (0, isEmpty_1.default)(val); | ||
} | ||
function isEmptyArr(val) { | ||
return (0, isArray_1.default)(val) && (0, isEmpty_1.default)(val); | ||
} | ||
/** | ||
* Does a comparison of each array value, plus a value equality check for empty | ||
* objects and arrays. | ||
*/ | ||
const areArrayValuesEqual = (arr1, arr2) => { | ||
if (arr1.length !== arr2.length) | ||
return false; | ||
return arr1.every((elem1, index) => { | ||
const elem2 = arr2[index]; | ||
/** | ||
* edge cases: if both values are empty object/array, we consider them equal. | ||
* These can catch empty default values (`[]`, `{}`) that unintentionally point | ||
* to different refernces. | ||
* | ||
* We can consider doing a deep comparison, but left it here for efficiency | ||
* + practicality for authenticator state comparison purposes. | ||
*/ | ||
if (isEmptyArr(elem1) && isEmptyArr(elem2)) | ||
return true; | ||
if (isEmptyObj(elem1) && isEmptyObj(elem2)) | ||
return true; | ||
return elem1 === elem2; | ||
}); | ||
}; | ||
exports.areArrayValuesEqual = areArrayValuesEqual; | ||
/** | ||
* AuthenticatorContext serves static reference to the auth machine service. | ||
* | ||
* https://xstate.js.org/docs/recipes/react.html#context-provider | ||
*/ | ||
exports.AuthenticatorContext = react_1.default.createContext({}); | ||
const Provider = ({ children, }) => { | ||
/** | ||
* Based on use cases, developer might already have added another Provider | ||
* outside Authenticator. In that case, we sync the two providers by just | ||
* passing the parent value. | ||
* | ||
* TODO(BREAKING): enforce only one provider in App tree | ||
*/ | ||
const parentProviderVal = react_1.default.useContext(exports.AuthenticatorContext); | ||
/** | ||
* Ideally, `useInterpret` shouldn't even be run if `parentProviderVal` is | ||
* not empty. But conditionally running `useInterpret` breaks rules of hooks. | ||
* | ||
* Leaving this as is for now in the interest of suggested code guideline. | ||
*/ | ||
const service = (0, react_2.useInterpret)(ui_1.createAuthenticatorMachine); | ||
const value = react_1.default.useMemo(() => ((0, isEmpty_1.default)(parentProviderVal) ? { service } : parentProviderVal), [parentProviderVal, service]); | ||
const { service: activeService } = value; | ||
react_1.default.useEffect(() => { | ||
return (0, ui_1.listenToAuthHub)(activeService); | ||
}, [activeService]); | ||
return (react_1.default.createElement(exports.AuthenticatorContext.Provider, { value: value }, children)); | ||
}; | ||
exports.Provider = Provider; | ||
const useAuthenticatorService = () => { | ||
const { service } = react_1.default.useContext(exports.AuthenticatorContext); | ||
if (!service) { | ||
throw new Error('Please ensure you wrap your App with `Authenticator.Provider`.\nSee the `useAuthenticator` section on https://ui.docs.amplify.aws/connected-components/authenticator.'); | ||
} | ||
return service; | ||
}; | ||
/** | ||
* [📖 Docs](https://ui.docs.amplify.aws/react/connected-components/authenticator/headless#useauthenticator-hook) | ||
*/ | ||
function useAuthenticator(selector) { | ||
const service = useAuthenticatorService(); | ||
const { send } = service; | ||
const getFacade = react_1.default.useCallback((state) => (Object.assign({}, (0, ui_1.getServiceFacade)({ send, state }))), [send]); | ||
/** | ||
* For `useSelector`'s selector argument, we transform `state` into | ||
* public facade values using `getFacade`. | ||
* | ||
* This is to hide the internal xstate implementation details to customers. | ||
*/ | ||
const xstateSelector = (state) => getFacade(state); | ||
/** | ||
* comparator decides whether or not the new authState should trigger a | ||
* re-render. Does a deep equality check. | ||
*/ | ||
const comparator = (prevFacade, nextFacade) => { | ||
if (!selector) | ||
return false; | ||
/** | ||
* Apply the passed in `selector` to get the value of their desired | ||
* dependency array. | ||
*/ | ||
const prevDepsArray = selector(prevFacade); | ||
const nextDepsArray = selector(nextFacade); | ||
// Shallow compare the array values | ||
// TODO: is there a reason to compare deep at the cost of expensive comparisons? | ||
return (0, exports.areArrayValuesEqual)(prevDepsArray, nextDepsArray); | ||
}; | ||
const facade = (0, react_2.useSelector)(service, xstateSelector, comparator); | ||
return Object.assign(Object.assign({}, facade), { | ||
/** @deprecated For internal use only */ | ||
_state: service.getSnapshot(), | ||
/** @deprecated For internal use only */ | ||
_send: send }); | ||
} | ||
exports.default = useAuthenticator; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const index_1 = require("../index"); | ||
describe('Haha', () => { | ||
it('should be truthy', () => { | ||
expect(index_1.Haha).toBeTruthy(); | ||
}); | ||
}); |
1 change: 1 addition & 0 deletions
1
packages/react-core/dist/esm/Authenticator/hooks/useAuthenticator/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default as useAuthenticator } from './useAuthenticator'; |
113 changes: 113 additions & 0 deletions
113
packages/react-core/dist/esm/Authenticator/hooks/useAuthenticator/useAuthenticator.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
// export default function useAuthenticator(): null { | ||
// return null; | ||
// } | ||
import React from 'react'; | ||
import { createAuthenticatorMachine, getServiceFacade, listenToAuthHub, } from '@aws-amplify/ui'; | ||
import { useSelector, useInterpret } from '@xstate/react'; | ||
import isEmpty from 'lodash/isEmpty'; | ||
import isArray from 'lodash/isArray'; | ||
import isObject from 'lodash/isObject'; | ||
function isEmptyObj(val) { | ||
return isObject(val) && isEmpty(val); | ||
} | ||
function isEmptyArr(val) { | ||
return isArray(val) && isEmpty(val); | ||
} | ||
/** | ||
* Does a comparison of each array value, plus a value equality check for empty | ||
* objects and arrays. | ||
*/ | ||
export const areArrayValuesEqual = (arr1, arr2) => { | ||
if (arr1.length !== arr2.length) | ||
return false; | ||
return arr1.every((elem1, index) => { | ||
const elem2 = arr2[index]; | ||
/** | ||
* edge cases: if both values are empty object/array, we consider them equal. | ||
* These can catch empty default values (`[]`, `{}`) that unintentionally point | ||
* to different refernces. | ||
* | ||
* We can consider doing a deep comparison, but left it here for efficiency | ||
* + practicality for authenticator state comparison purposes. | ||
*/ | ||
if (isEmptyArr(elem1) && isEmptyArr(elem2)) | ||
return true; | ||
if (isEmptyObj(elem1) && isEmptyObj(elem2)) | ||
return true; | ||
return elem1 === elem2; | ||
}); | ||
}; | ||
/** | ||
* AuthenticatorContext serves static reference to the auth machine service. | ||
* | ||
* https://xstate.js.org/docs/recipes/react.html#context-provider | ||
*/ | ||
export const AuthenticatorContext = React.createContext({}); | ||
export const Provider = ({ children, }) => { | ||
/** | ||
* Based on use cases, developer might already have added another Provider | ||
* outside Authenticator. In that case, we sync the two providers by just | ||
* passing the parent value. | ||
* | ||
* TODO(BREAKING): enforce only one provider in App tree | ||
*/ | ||
const parentProviderVal = React.useContext(AuthenticatorContext); | ||
/** | ||
* Ideally, `useInterpret` shouldn't even be run if `parentProviderVal` is | ||
* not empty. But conditionally running `useInterpret` breaks rules of hooks. | ||
* | ||
* Leaving this as is for now in the interest of suggested code guideline. | ||
*/ | ||
const service = useInterpret(createAuthenticatorMachine); | ||
const value = React.useMemo(() => (isEmpty(parentProviderVal) ? { service } : parentProviderVal), [parentProviderVal, service]); | ||
const { service: activeService } = value; | ||
React.useEffect(() => { | ||
return listenToAuthHub(activeService); | ||
}, [activeService]); | ||
return (React.createElement(AuthenticatorContext.Provider, { value: value }, children)); | ||
}; | ||
const useAuthenticatorService = () => { | ||
const { service } = React.useContext(AuthenticatorContext); | ||
if (!service) { | ||
throw new Error('Please ensure you wrap your App with `Authenticator.Provider`.\nSee the `useAuthenticator` section on https://ui.docs.amplify.aws/connected-components/authenticator.'); | ||
} | ||
return service; | ||
}; | ||
/** | ||
* [📖 Docs](https://ui.docs.amplify.aws/react/connected-components/authenticator/headless#useauthenticator-hook) | ||
*/ | ||
export default function useAuthenticator(selector) { | ||
const service = useAuthenticatorService(); | ||
const { send } = service; | ||
const getFacade = React.useCallback((state) => (Object.assign({}, getServiceFacade({ send, state }))), [send]); | ||
/** | ||
* For `useSelector`'s selector argument, we transform `state` into | ||
* public facade values using `getFacade`. | ||
* | ||
* This is to hide the internal xstate implementation details to customers. | ||
*/ | ||
const xstateSelector = (state) => getFacade(state); | ||
/** | ||
* comparator decides whether or not the new authState should trigger a | ||
* re-render. Does a deep equality check. | ||
*/ | ||
const comparator = (prevFacade, nextFacade) => { | ||
if (!selector) | ||
return false; | ||
/** | ||
* Apply the passed in `selector` to get the value of their desired | ||
* dependency array. | ||
*/ | ||
const prevDepsArray = selector(prevFacade); | ||
const nextDepsArray = selector(nextFacade); | ||
// Shallow compare the array values | ||
// TODO: is there a reason to compare deep at the cost of expensive comparisons? | ||
return areArrayValuesEqual(prevDepsArray, nextDepsArray); | ||
}; | ||
const facade = useSelector(service, xstateSelector, comparator); | ||
return Object.assign(Object.assign({}, facade), { | ||
/** @deprecated For internal use only */ | ||
_state: service.getSnapshot(), | ||
/** @deprecated For internal use only */ | ||
_send: send }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { Haha } from '../index'; | ||
describe('Haha', () => { | ||
it('should be truthy', () => { | ||
expect(Haha).toBeTruthy(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const Haha = 'haha'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Haha = void 0; | ||
exports.Haha = 'haha'; |
1 change: 1 addition & 0 deletions
1
packages/react-core/dist/types/Authenticator/hooks/useAuthenticator/index.d.ts
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
50 changes: 50 additions & 0 deletions
50
packages/react-core/dist/types/Authenticator/hooks/useAuthenticator/useAuthenticator.d.ts
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters