@aws-amplify/ui-react-core 3.4.0 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,26 @@
1
+ import React__default from 'react';
2
+ import { isUndefined } from '@aws-amplify/ui';
3
+ import useHasValueUpdated from './useHasValueUpdated.mjs';
4
+
5
+ function useControlledReducer(reducer, initialState, options) {
6
+ const { controlledState, onStateChange } = options ?? {};
7
+ const [uncontrolledState, dispatch] = React__default.useReducer(reducer, controlledState ?? initialState);
8
+ const controlledStateRef = React__default.useRef();
9
+ if (!isUndefined(controlledState)) {
10
+ controlledStateRef.current = controlledState;
11
+ }
12
+ const hasUncontrolledStateChanged = useHasValueUpdated(uncontrolledState, true);
13
+ React__default.useEffect(() => {
14
+ // only run `onStateChange` if `uncontrolledState` has changed,
15
+ // ignore reference change to `onStateChange`
16
+ if (hasUncontrolledStateChanged) {
17
+ onStateChange?.(uncontrolledState);
18
+ }
19
+ }, [hasUncontrolledStateChanged, onStateChange, uncontrolledState]);
20
+ const state = controlledStateRef.current
21
+ ? controlledStateRef.current
22
+ : uncontrolledState;
23
+ return React__default.useMemo(() => [state, dispatch], [state]);
24
+ }
25
+
26
+ export { useControlledReducer as default };
@@ -10,11 +10,12 @@ export { default as FormProvider } from './components/FormCore/FormProvider.mjs'
10
10
  export { default as withFormProvider } from './components/FormCore/withFormProvider.mjs';
11
11
  export { default as RenderNothing } from './components/RenderNothing/RenderNothing.mjs';
12
12
  export { default as useDataState } from './hooks/useDataState/useDataState.mjs';
13
+ export { default as useControlledReducer } from './hooks/useControlledReducer.mjs';
14
+ export { default as useDropZone } from './hooks/useDropZone.mjs';
13
15
  export { default as useDeprecationWarning } from './hooks/useDeprecationWarning.mjs';
14
16
  export { default as useGetUrl } from './hooks/useGetUrl.mjs';
15
17
  export { default as useHasValueUpdated } from './hooks/useHasValueUpdated.mjs';
16
18
  export { default as usePreviousValue } from './hooks/usePreviousValue.mjs';
17
19
  export { default as useSetUserAgent } from './hooks/useSetUserAgent.mjs';
18
20
  export { default as useTimeout } from './hooks/useTimeout.mjs';
19
- export { default as useDropZone } from './hooks/useDropZone.mjs';
20
21
  export { default as createContextUtilities } from './utils/createContextUtilities.mjs';
package/dist/index.js CHANGED
@@ -649,55 +649,6 @@ function useDataState(action, initialData, options) {
649
649
  return [dataState, handleAction];
650
650
  }
651
651
 
652
- /**
653
- * Logs a deprecation warning message.
654
- *
655
- * @important Please use the React/React Native specific platform implementations.
656
- * This version of the hook is a base implementation that the others extend from due
657
- * to env differences between running in RN or the browser
658
- */
659
- const useDeprecationWarning = ({ shouldWarn, message, }) => {
660
- React__namespace.useEffect(() => {
661
- if (shouldWarn) {
662
- // eslint-disable-next-line no-console
663
- console.warn(message);
664
- }
665
- }, [shouldWarn, message]);
666
- };
667
-
668
- const INIT_STATE = {
669
- url: undefined,
670
- expiresAt: undefined,
671
- isLoading: true,
672
- };
673
- function useGetUrl(input) {
674
- const [result, setResult] = React__namespace.useState(() => INIT_STATE);
675
- React__namespace.useEffect(() => {
676
- const { onError, ...getUrlInput } = input;
677
- let ignore = false;
678
- storage.getUrl(getUrlInput)
679
- .then((response) => {
680
- if (ignore) {
681
- return;
682
- }
683
- setResult({ ...response, isLoading: false });
684
- })
685
- .catch((error) => {
686
- if (ignore) {
687
- return;
688
- }
689
- if (ui.isFunction(onError)) {
690
- onError(error);
691
- }
692
- setResult({ ...INIT_STATE, isLoading: false });
693
- });
694
- return () => {
695
- ignore = true;
696
- };
697
- }, [input]);
698
- return result;
699
- }
700
-
701
652
  function usePreviousValue(value) {
702
653
  const previous = React.useRef(undefined);
703
654
  // update ref post render
@@ -730,33 +681,25 @@ function useHasValueUpdated(value, ignoreFirstRender = false) {
730
681
  return previous !== value;
731
682
  }
732
683
 
733
- function useSetUserAgent({ componentName, packageName, version, }) {
734
- React.useEffect(() => {
735
- const clearUserAgent = ui.setUserAgent({
736
- componentName,
737
- packageName,
738
- version,
739
- });
740
- return clearUserAgent;
741
- }, [componentName, packageName, version]);
742
- }
743
-
744
- function useTimeout({ callback, delay, }) {
745
- const storedCallback = React__namespace["default"].useRef(callback);
746
- React__namespace["default"].useLayoutEffect(() => {
747
- storedCallback.current = callback;
748
- }, [callback]);
684
+ function useControlledReducer(reducer, initialState, options) {
685
+ const { controlledState, onStateChange } = options ?? {};
686
+ const [uncontrolledState, dispatch] = React__namespace["default"].useReducer(reducer, controlledState ?? initialState);
687
+ const controlledStateRef = React__namespace["default"].useRef();
688
+ if (!ui.isUndefined(controlledState)) {
689
+ controlledStateRef.current = controlledState;
690
+ }
691
+ const hasUncontrolledStateChanged = useHasValueUpdated(uncontrolledState, true);
749
692
  React__namespace["default"].useEffect(() => {
750
- if (!ui.isFunction(storedCallback.current) || !delay) {
751
- return;
693
+ // only run `onStateChange` if `uncontrolledState` has changed,
694
+ // ignore reference change to `onStateChange`
695
+ if (hasUncontrolledStateChanged) {
696
+ onStateChange?.(uncontrolledState);
752
697
  }
753
- const timeoutId = setTimeout(() => {
754
- storedCallback.current?.();
755
- }, delay);
756
- return () => {
757
- clearTimeout(timeoutId);
758
- };
759
- }, [delay]);
698
+ }, [hasUncontrolledStateChanged, onStateChange, uncontrolledState]);
699
+ const state = controlledStateRef.current
700
+ ? controlledStateRef.current
701
+ : uncontrolledState;
702
+ return React__namespace["default"].useMemo(() => [state, dispatch], [state]);
760
703
  }
761
704
 
762
705
  function filterAllowedFiles(files, acceptedFileTypes) {
@@ -925,6 +868,84 @@ function useDropZone({ onDropComplete, onDragEnter: _onDragEnter, onDragLeave: _
925
868
  };
926
869
  }
927
870
 
871
+ /**
872
+ * Logs a deprecation warning message.
873
+ *
874
+ * @important Please use the React/React Native specific platform implementations.
875
+ * This version of the hook is a base implementation that the others extend from due
876
+ * to env differences between running in RN or the browser
877
+ */
878
+ const useDeprecationWarning = ({ shouldWarn, message, }) => {
879
+ React__namespace.useEffect(() => {
880
+ if (shouldWarn) {
881
+ // eslint-disable-next-line no-console
882
+ console.warn(message);
883
+ }
884
+ }, [shouldWarn, message]);
885
+ };
886
+
887
+ const INIT_STATE = {
888
+ url: undefined,
889
+ expiresAt: undefined,
890
+ isLoading: true,
891
+ };
892
+ function useGetUrl(input) {
893
+ const [result, setResult] = React__namespace.useState(() => INIT_STATE);
894
+ React__namespace.useEffect(() => {
895
+ const { onError, ...getUrlInput } = input;
896
+ let ignore = false;
897
+ storage.getUrl(getUrlInput)
898
+ .then((response) => {
899
+ if (ignore) {
900
+ return;
901
+ }
902
+ setResult({ ...response, isLoading: false });
903
+ })
904
+ .catch((error) => {
905
+ if (ignore) {
906
+ return;
907
+ }
908
+ if (ui.isFunction(onError)) {
909
+ onError(error);
910
+ }
911
+ setResult({ ...INIT_STATE, isLoading: false });
912
+ });
913
+ return () => {
914
+ ignore = true;
915
+ };
916
+ }, [input]);
917
+ return result;
918
+ }
919
+
920
+ function useSetUserAgent({ componentName, packageName, version, }) {
921
+ React.useEffect(() => {
922
+ const clearUserAgent = ui.setUserAgent({
923
+ componentName,
924
+ packageName,
925
+ version,
926
+ });
927
+ return clearUserAgent;
928
+ }, [componentName, packageName, version]);
929
+ }
930
+
931
+ function useTimeout({ callback, delay, }) {
932
+ const storedCallback = React__namespace["default"].useRef(callback);
933
+ React__namespace["default"].useLayoutEffect(() => {
934
+ storedCallback.current = callback;
935
+ }, [callback]);
936
+ React__namespace["default"].useEffect(() => {
937
+ if (!ui.isFunction(storedCallback.current) || !delay) {
938
+ return;
939
+ }
940
+ const timeoutId = setTimeout(() => {
941
+ storedCallback.current?.();
942
+ }, delay);
943
+ return () => {
944
+ clearTimeout(timeoutId);
945
+ };
946
+ }, [delay]);
947
+ }
948
+
928
949
  const INVALID_OPTIONS_MESSAGE = 'an `errorMessage` or a `defaultValue` must be provided in `options`';
929
950
  /**
930
951
  * Uses `ContextType`/`Name` generics and `options` to create:
@@ -1013,6 +1034,7 @@ exports.resolveAuthenticatorComponents = resolveAuthenticatorComponents;
1013
1034
  exports.useAuthenticator = useAuthenticator;
1014
1035
  exports.useAuthenticatorInitMachine = useAuthenticatorInitMachine;
1015
1036
  exports.useAuthenticatorRoute = useAuthenticatorRoute;
1037
+ exports.useControlledReducer = useControlledReducer;
1016
1038
  exports.useDataState = useDataState;
1017
1039
  exports.useDeprecationWarning = useDeprecationWarning;
1018
1040
  exports.useDropZone = useDropZone;
@@ -1,8 +1,9 @@
1
1
  export { useDataState, AsyncDataAction, DataAction, DataState, } from './useDataState';
2
+ export { default as useControlledReducer } from './useControlledReducer';
3
+ export { default as useDropZone, UseDropZoneParams } from './useDropZone';
2
4
  export { default as useDeprecationWarning, UseDeprecationWarning, } from './useDeprecationWarning';
3
5
  export { default as useGetUrl } from './useGetUrl';
4
6
  export { default as useHasValueUpdated } from './useHasValueUpdated';
5
7
  export { default as usePreviousValue } from './usePreviousValue';
6
8
  export { default as useSetUserAgent } from './useSetUserAgent';
7
9
  export { default as useTimeout } from './useTimeout';
8
- export { default as useDropZone, UseDropZoneParams } from './useDropZone';
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ export default function useControlledReducer<R extends React.Reducer<any, any>, S extends React.ReducerState<R>>(reducer: R, initialState: S, options?: {
3
+ controlledState?: S;
4
+ onStateChange?: (state: S) => void;
5
+ }): [S, React.Dispatch<React.ReducerAction<R>>];
@@ -1,5 +1,5 @@
1
1
  export { AuthenticatorComponentDefaults, AuthenticatorComponentDefaultProps, AuthenticatorComponentOverrides, AuthenticatorFooterComponent, AuthenticatorFormFieldsComponent, AuthenticatorHeaderComponent, AuthenticatorLegacyField, AuthenticatorMachineContext, AuthenticatorProvider, AuthenticatorRouteComponentKey, AuthenticatorRouteComponentName, isAuthenticatorComponentRouteKey, resolveAuthenticatorComponents, useAuthenticator, useAuthenticatorRoute, UseAuthenticator, useAuthenticatorInitMachine, UseAuthenticatorRoute, } from './Authenticator';
2
2
  export { FormProvider, FormProviderProps, RenderNothing, FormValues, FormHandle, useField, useForm, UseForm, Validate, Validator, withFormProvider, } from './components';
3
- export { AsyncDataAction, DataAction, useDeprecationWarning, UseDeprecationWarning, useGetUrl, useHasValueUpdated, usePreviousValue, useSetUserAgent, useTimeout, useDataState, DataState, useDropZone, UseDropZoneParams, } from './hooks';
3
+ export { AsyncDataAction, DataAction, useControlledReducer, useDeprecationWarning, UseDeprecationWarning, useGetUrl, useHasValueUpdated, usePreviousValue, useSetUserAgent, useTimeout, useDataState, DataState, useDropZone, UseDropZoneParams, } from './hooks';
4
4
  export { MergeProps } from './types';
5
5
  export { createContextUtilities } from './utils';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aws-amplify/ui-react-core",
3
- "version": "3.4.0",
3
+ "version": "3.4.1",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/esm/index.mjs",
6
6
  "react-native": "src/index.ts",
@@ -40,7 +40,7 @@
40
40
  "typecheck": "tsc --noEmit"
41
41
  },
42
42
  "dependencies": {
43
- "@aws-amplify/ui": "6.10.0",
43
+ "@aws-amplify/ui": "6.10.1",
44
44
  "@xstate/react": "^3.2.2",
45
45
  "lodash": "4.17.21",
46
46
  "react-hook-form": "^7.53.2",
@@ -5,6 +5,8 @@ export {
5
5
  DataState,
6
6
  } from './useDataState';
7
7
 
8
+ export { default as useControlledReducer } from './useControlledReducer';
9
+ export { default as useDropZone, UseDropZoneParams } from './useDropZone';
8
10
  export {
9
11
  default as useDeprecationWarning,
10
12
  UseDeprecationWarning,
@@ -14,4 +16,3 @@ export { default as useHasValueUpdated } from './useHasValueUpdated';
14
16
  export { default as usePreviousValue } from './usePreviousValue';
15
17
  export { default as useSetUserAgent } from './useSetUserAgent';
16
18
  export { default as useTimeout } from './useTimeout';
17
- export { default as useDropZone, UseDropZoneParams } from './useDropZone';
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { isUndefined } from '@aws-amplify/ui';
3
+
4
+ import useHasValueUpdated from './useHasValueUpdated';
5
+
6
+ export default function useControlledReducer<
7
+ R extends React.Reducer<any, any>,
8
+ S extends React.ReducerState<R>,
9
+ >(
10
+ reducer: R,
11
+ initialState: S,
12
+ options?: {
13
+ controlledState?: S;
14
+ onStateChange?: (state: S) => void;
15
+ }
16
+ ): [S, React.Dispatch<React.ReducerAction<R>>] {
17
+ const { controlledState, onStateChange } = options ?? {};
18
+
19
+ const [uncontrolledState, dispatch] = React.useReducer(
20
+ reducer,
21
+ controlledState ?? initialState
22
+ );
23
+
24
+ const controlledStateRef = React.useRef<S | undefined>();
25
+ if (!isUndefined(controlledState)) {
26
+ controlledStateRef.current = controlledState;
27
+ }
28
+
29
+ const hasUncontrolledStateChanged = useHasValueUpdated(
30
+ uncontrolledState,
31
+ true
32
+ );
33
+ React.useEffect(() => {
34
+ // only run `onStateChange` if `uncontrolledState` has changed,
35
+ // ignore reference change to `onStateChange`
36
+ if (hasUncontrolledStateChanged) {
37
+ onStateChange?.(uncontrolledState);
38
+ }
39
+ }, [hasUncontrolledStateChanged, onStateChange, uncontrolledState]);
40
+
41
+ const state = controlledStateRef.current
42
+ ? controlledStateRef.current
43
+ : uncontrolledState;
44
+
45
+ return React.useMemo(() => [state, dispatch], [state]);
46
+ }
package/src/index.ts CHANGED
@@ -37,6 +37,7 @@ export {
37
37
  export {
38
38
  AsyncDataAction,
39
39
  DataAction,
40
+ useControlledReducer,
40
41
  useDeprecationWarning,
41
42
  UseDeprecationWarning,
42
43
  useGetUrl,