Skip to content

Commit

Permalink
chore(rwa): remove user destructure in getServiceContextFacade
Browse files Browse the repository at this point in the history
  • Loading branch information
calebpollman committed Aug 31, 2022
1 parent 4e2c13e commit f0d762d
Show file tree
Hide file tree
Showing 13 changed files with 315 additions and 1 deletion.
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; } });
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;
8 changes: 8 additions & 0 deletions packages/react-core/dist/__tests__/index.spec.js
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();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as useAuthenticator } from './useAuthenticator';
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 });
}
6 changes: 6 additions & 0 deletions packages/react-core/dist/esm/__tests__/index.spec.js
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();
});
});
1 change: 1 addition & 0 deletions packages/react-core/dist/esm/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const Haha = 'haha';
4 changes: 4 additions & 0 deletions packages/react-core/dist/index.js
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';

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/react-core/dist/types/__tests__/index.spec.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/react-core/dist/types/index.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/ui/src/helpers/authenticator/facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export const getServiceContextFacade = (

// check for user in actorContext prior to state context. actorContext is more "up to date",
// but is not available on all states
const { user } = actorContext ?? state.context;
const user = actorContext?.user ?? state.context?.user;

const hasValidationErrors =
validationErrors && Object.keys(validationErrors).length > 0;
Expand Down

0 comments on commit f0d762d

Please sign in to comment.