@logto/react 2.2.4 → 3.0.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.
package/README.md CHANGED
@@ -1,9 +1,12 @@
1
1
  # Logto React SDK
2
+
2
3
  [![Version](https://img.shields.io/npm/v/@logto/react)](https://www.npmjs.com/package/@logto/react)
3
4
  [![Build Status](https://github.com/logto-io/js/actions/workflows/main.yml/badge.svg)](https://github.com/logto-io/js/actions/workflows/main.yml)
4
5
  [![Codecov](https://img.shields.io/codecov/c/github/logto-io/js)](https://app.codecov.io/gh/logto-io/js?branch=master)
5
6
 
6
- The Logto React SDK written in TypeScript. Check out our [integration guide](https://docs.logto.io/docs/recipes/integrate-logto/react) or [docs](https://docs.logto.io/sdk/JavaScript/react/) for more information.
7
+ The Logto React SDK written in TypeScript.
8
+
9
+ Check out our [docs](https://docs.logto.io/sdk/react/) for more information.
7
10
 
8
11
  ## Installation
9
12
 
@@ -29,14 +32,15 @@ pnpm add @logto/react
29
32
 
30
33
  A sample project can be found at [React Sample](https://github.com/logto-io/js/tree/master/packages/react-sample)
31
34
 
32
- Check out the source code and try it with ease.
35
+ Check out the full JS repo and try it with ease.
33
36
 
34
- ```
35
- pnpm i && pnpm start
37
+ ```bash
38
+ pnpm i && pnpm build
39
+ cd packages/react-sample && pnpm start
36
40
  ```
37
41
 
38
42
  ## Resources
39
43
 
40
44
  [![Website](https://img.shields.io/badge/website-logto.io-8262F8.svg)](https://logto.io/)
41
- [![Docs](https://img.shields.io/badge/docs-logto.io-green.svg)](https://docs.logto.io/sdk/JavaScript/react/)
45
+ [![Docs](https://img.shields.io/badge/docs-logto.io-green.svg)](https://docs.logto.io/)
42
46
  [![Discord](https://img.shields.io/discord/965845662535147551?logo=discord&logoColor=ffffff&color=7389D8&cacheSeconds=600)](https://discord.gg/UEPaF3j5e6)
package/lib/context.cjs CHANGED
@@ -5,13 +5,19 @@ var react = require('react');
5
5
  const throwContextError = () => {
6
6
  throw new Error('Must be used inside <LogtoProvider> context.');
7
7
  };
8
+ /**
9
+ * The context for the LogtoProvider.
10
+ *
11
+ * @remarks
12
+ * Instead of using this context directly, in most cases you should use the `useLogto` hook.
13
+ */
8
14
  const LogtoContext = react.createContext({
9
15
  logtoClient: undefined,
10
16
  isAuthenticated: false,
11
- loadingCount: 0,
17
+ isLoading: false,
12
18
  error: undefined,
13
19
  setIsAuthenticated: throwContextError,
14
- setLoadingCount: throwContextError,
20
+ setIsLoading: throwContextError,
15
21
  setError: throwContextError,
16
22
  });
17
23
 
package/lib/context.d.ts CHANGED
@@ -1,13 +1,36 @@
1
1
  /// <reference types="react" />
2
2
  import type LogtoClient from '@logto/browser';
3
3
  export type LogtoContextProps = {
4
+ /** The underlying LogtoClient instance (from `@logto/browser`). */
4
5
  logtoClient?: LogtoClient;
6
+ /** Whether the user is authenticated or not. */
5
7
  isAuthenticated: boolean;
6
- loadingCount: number;
8
+ /** Whether the context has any pending requests. It will be `true` if there is at least one request pending. */
9
+ isLoading: boolean;
10
+ /** The error that occurred during the last request. If there was no error, this will be `undefined`. */
7
11
  error?: Error;
12
+ /** Sets the authentication state. */
8
13
  setIsAuthenticated: React.Dispatch<React.SetStateAction<boolean>>;
9
- setLoadingCount: React.Dispatch<React.SetStateAction<number>>;
14
+ /**
15
+ * Sets the loading state.
16
+ *
17
+ * @remarks
18
+ * Instead of directly setting the boolean value, this function will increment or decrement the
19
+ * loading count.
20
+ *
21
+ * - If the `state` is `true`, the loading count will be incremented by 1.
22
+ * - If the `state` is `false`, the loading count will be decremented by 1. If the loading count
23
+ * is already 0, it will be set to 0.
24
+ */
25
+ setIsLoading: (state: boolean) => void;
26
+ /** Sets the error state. To clear the error, set this to `undefined`. */
10
27
  setError: React.Dispatch<React.SetStateAction<Error | undefined>>;
11
28
  };
12
29
  export declare const throwContextError: () => never;
30
+ /**
31
+ * The context for the LogtoProvider.
32
+ *
33
+ * @remarks
34
+ * Instead of using this context directly, in most cases you should use the `useLogto` hook.
35
+ */
13
36
  export declare const LogtoContext: import("react").Context<LogtoContextProps>;
package/lib/context.js CHANGED
@@ -3,13 +3,19 @@ import { createContext } from 'react';
3
3
  const throwContextError = () => {
4
4
  throw new Error('Must be used inside <LogtoProvider> context.');
5
5
  };
6
+ /**
7
+ * The context for the LogtoProvider.
8
+ *
9
+ * @remarks
10
+ * Instead of using this context directly, in most cases you should use the `useLogto` hook.
11
+ */
6
12
  const LogtoContext = createContext({
7
13
  logtoClient: undefined,
8
14
  isAuthenticated: false,
9
- loadingCount: 0,
15
+ isLoading: false,
10
16
  error: undefined,
11
17
  setIsAuthenticated: throwContextError,
12
- setLoadingCount: throwContextError,
18
+ setIsLoading: throwContextError,
13
19
  setError: throwContextError,
14
20
  });
15
21
 
@@ -4,19 +4,6 @@ var essentials = require('@silverhand/essentials');
4
4
  var react = require('react');
5
5
  var context = require('../context.cjs');
6
6
 
7
- const useLoadingState = () => {
8
- const { loadingCount, setLoadingCount } = react.useContext(context.LogtoContext);
9
- const isLoading = loadingCount > 0;
10
- const setLoadingState = react.useCallback((state) => {
11
- if (state) {
12
- setLoadingCount((count) => count + 1);
13
- }
14
- else {
15
- setLoadingCount((count) => Math.max(0, count - 1));
16
- }
17
- }, [setLoadingCount]);
18
- return { isLoading, setLoadingState };
19
- };
20
7
  const useErrorHandler = () => {
21
8
  const { setError } = react.useContext(context.LogtoContext);
22
9
  const handleError = react.useCallback((error, fallbackErrorMessage) => {
@@ -31,8 +18,7 @@ const useErrorHandler = () => {
31
18
  return { handleError };
32
19
  };
33
20
  const useHandleSignInCallback = (callback) => {
34
- const { logtoClient, isAuthenticated, error, setIsAuthenticated } = react.useContext(context.LogtoContext);
35
- const { isLoading, setLoadingState } = useLoadingState();
21
+ const { logtoClient, isAuthenticated, error, setIsAuthenticated, isLoading, setIsLoading } = react.useContext(context.LogtoContext);
36
22
  const { handleError } = useErrorHandler();
37
23
  const callbackRef = react.useRef();
38
24
  react.useEffect(() => {
@@ -47,7 +33,7 @@ const useHandleSignInCallback = (callback) => {
47
33
  const currentPageUrl = window.location.href;
48
34
  const isRedirected = await logtoClient.isSignInRedirected(currentPageUrl);
49
35
  if (!isAuthenticated && isRedirected) {
50
- setLoadingState(true);
36
+ setIsLoading(true);
51
37
  await essentials.trySafe(async () => {
52
38
  await logtoClient.handleSignInCallback(currentPageUrl);
53
39
  setIsAuthenticated(true);
@@ -55,7 +41,7 @@ const useHandleSignInCallback = (callback) => {
55
41
  }, (error) => {
56
42
  handleError(error, 'Unexpected error occurred while handling sign in callback.');
57
43
  });
58
- setLoadingState(false);
44
+ setIsLoading(false);
59
45
  }
60
46
  })();
61
47
  }, [
@@ -65,7 +51,7 @@ const useHandleSignInCallback = (callback) => {
65
51
  isLoading,
66
52
  logtoClient,
67
53
  setIsAuthenticated,
68
- setLoadingState,
54
+ setIsLoading,
69
55
  ]);
70
56
  return {
71
57
  isLoading,
@@ -74,15 +60,13 @@ const useHandleSignInCallback = (callback) => {
74
60
  };
75
61
  };
76
62
  const useLogto = () => {
77
- const { logtoClient, loadingCount, isAuthenticated, error } = react.useContext(context.LogtoContext);
78
- const { setLoadingState } = useLoadingState();
63
+ const { logtoClient, isAuthenticated, error, isLoading, setIsLoading } = react.useContext(context.LogtoContext);
79
64
  const { handleError } = useErrorHandler();
80
- const isLoading = loadingCount > 0;
81
65
  const client = logtoClient ?? context.throwContextError();
82
66
  const proxy = react.useCallback((run, resetLoadingState = true) => {
83
67
  return async (...args) => {
84
68
  try {
85
- setLoadingState(true);
69
+ setIsLoading(true);
86
70
  return await run(...args);
87
71
  }
88
72
  catch (error) {
@@ -90,11 +74,11 @@ const useLogto = () => {
90
74
  }
91
75
  finally {
92
76
  if (resetLoadingState) {
93
- setLoadingState(false);
77
+ setIsLoading(false);
94
78
  }
95
79
  }
96
80
  };
97
- }, [setLoadingState, handleError]);
81
+ }, [setIsLoading, handleError]);
98
82
  const methods = react.useMemo(() => ({
99
83
  getRefreshToken: proxy(client.getRefreshToken.bind(client)),
100
84
  getAccessToken: proxy(client.getAccessToken.bind(client)),
@@ -2,19 +2,6 @@ import { trySafe } from '@silverhand/essentials';
2
2
  import { useContext, useRef, useEffect, useCallback, useMemo } from 'react';
3
3
  import { LogtoContext, throwContextError } from '../context.js';
4
4
 
5
- const useLoadingState = () => {
6
- const { loadingCount, setLoadingCount } = useContext(LogtoContext);
7
- const isLoading = loadingCount > 0;
8
- const setLoadingState = useCallback((state) => {
9
- if (state) {
10
- setLoadingCount((count) => count + 1);
11
- }
12
- else {
13
- setLoadingCount((count) => Math.max(0, count - 1));
14
- }
15
- }, [setLoadingCount]);
16
- return { isLoading, setLoadingState };
17
- };
18
5
  const useErrorHandler = () => {
19
6
  const { setError } = useContext(LogtoContext);
20
7
  const handleError = useCallback((error, fallbackErrorMessage) => {
@@ -29,8 +16,7 @@ const useErrorHandler = () => {
29
16
  return { handleError };
30
17
  };
31
18
  const useHandleSignInCallback = (callback) => {
32
- const { logtoClient, isAuthenticated, error, setIsAuthenticated } = useContext(LogtoContext);
33
- const { isLoading, setLoadingState } = useLoadingState();
19
+ const { logtoClient, isAuthenticated, error, setIsAuthenticated, isLoading, setIsLoading } = useContext(LogtoContext);
34
20
  const { handleError } = useErrorHandler();
35
21
  const callbackRef = useRef();
36
22
  useEffect(() => {
@@ -45,7 +31,7 @@ const useHandleSignInCallback = (callback) => {
45
31
  const currentPageUrl = window.location.href;
46
32
  const isRedirected = await logtoClient.isSignInRedirected(currentPageUrl);
47
33
  if (!isAuthenticated && isRedirected) {
48
- setLoadingState(true);
34
+ setIsLoading(true);
49
35
  await trySafe(async () => {
50
36
  await logtoClient.handleSignInCallback(currentPageUrl);
51
37
  setIsAuthenticated(true);
@@ -53,7 +39,7 @@ const useHandleSignInCallback = (callback) => {
53
39
  }, (error) => {
54
40
  handleError(error, 'Unexpected error occurred while handling sign in callback.');
55
41
  });
56
- setLoadingState(false);
42
+ setIsLoading(false);
57
43
  }
58
44
  })();
59
45
  }, [
@@ -63,7 +49,7 @@ const useHandleSignInCallback = (callback) => {
63
49
  isLoading,
64
50
  logtoClient,
65
51
  setIsAuthenticated,
66
- setLoadingState,
52
+ setIsLoading,
67
53
  ]);
68
54
  return {
69
55
  isLoading,
@@ -72,15 +58,13 @@ const useHandleSignInCallback = (callback) => {
72
58
  };
73
59
  };
74
60
  const useLogto = () => {
75
- const { logtoClient, loadingCount, isAuthenticated, error } = useContext(LogtoContext);
76
- const { setLoadingState } = useLoadingState();
61
+ const { logtoClient, isAuthenticated, error, isLoading, setIsLoading } = useContext(LogtoContext);
77
62
  const { handleError } = useErrorHandler();
78
- const isLoading = loadingCount > 0;
79
63
  const client = logtoClient ?? throwContextError();
80
64
  const proxy = useCallback((run, resetLoadingState = true) => {
81
65
  return async (...args) => {
82
66
  try {
83
- setLoadingState(true);
67
+ setIsLoading(true);
84
68
  return await run(...args);
85
69
  }
86
70
  catch (error) {
@@ -88,11 +72,11 @@ const useLogto = () => {
88
72
  }
89
73
  finally {
90
74
  if (resetLoadingState) {
91
- setLoadingState(false);
75
+ setIsLoading(false);
92
76
  }
93
77
  }
94
78
  };
95
- }, [setLoadingState, handleError]);
79
+ }, [setIsLoading, handleError]);
96
80
  const methods = useMemo(() => ({
97
81
  getRefreshToken: proxy(client.getRefreshToken.bind(client)),
98
82
  getAccessToken: proxy(client.getAccessToken.bind(client)),
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { LogtoContextProps } from './context.js';
1
+ export type { LogtoContextProps, LogtoContext } from './context.js';
2
2
  export type { LogtoConfig, IdTokenClaims, UserInfoResponse, LogtoErrorCode, LogtoClientErrorCode, InteractionMode, } from '@logto/browser';
3
3
  export { LogtoError, LogtoRequestError, LogtoClientError, OidcError, Prompt, ReservedScope, ReservedResource, UserScope, organizationUrnPrefix, buildOrganizationUrn, getOrganizationIdFromUrn, PersistKey, } from '@logto/browser';
4
4
  export * from './provider.js';
package/lib/provider.cjs CHANGED
@@ -14,6 +14,15 @@ const LogtoProvider = ({ config, children, unstable_enableCache = false, }) => {
14
14
  const memorizedLogtoClient = react.useMemo(() => ({ logtoClient: new LogtoClient__default.default(config, unstable_enableCache) }), [config, unstable_enableCache]);
15
15
  const [isAuthenticated, setIsAuthenticated] = react.useState(false);
16
16
  const [error, setError] = react.useState();
17
+ const isLoading = react.useMemo(() => loadingCount > 0, [loadingCount]);
18
+ const setIsLoading = react.useCallback((state) => {
19
+ if (state) {
20
+ setLoadingCount((count) => count + 1);
21
+ }
22
+ else {
23
+ setLoadingCount((count) => Math.max(0, count - 1));
24
+ }
25
+ }, [setLoadingCount]);
17
26
  react.useEffect(() => {
18
27
  (async () => {
19
28
  const isAuthenticated = await memorizedLogtoClient.logtoClient.isAuthenticated();
@@ -25,11 +34,11 @@ const LogtoProvider = ({ config, children, unstable_enableCache = false, }) => {
25
34
  ...memorizedLogtoClient,
26
35
  isAuthenticated,
27
36
  setIsAuthenticated,
28
- loadingCount,
29
- setLoadingCount,
37
+ isLoading,
38
+ setIsLoading,
30
39
  error,
31
40
  setError,
32
- }), [memorizedLogtoClient, isAuthenticated, loadingCount, error]);
41
+ }), [memorizedLogtoClient, isAuthenticated, isLoading, setIsLoading, error]);
33
42
  return jsxRuntime.jsx(context.LogtoContext.Provider, { value: memorizedContextValue, children: children });
34
43
  };
35
44
 
package/lib/provider.d.ts CHANGED
@@ -9,4 +9,4 @@ export type LogtoProviderProps = {
9
9
  unstable_enableCache?: boolean;
10
10
  children?: ReactNode;
11
11
  };
12
- export declare const LogtoProvider: ({ config, children, unstable_enableCache, }: LogtoProviderProps) => JSX.Element;
12
+ export declare const LogtoProvider: ({ config, children, unstable_enableCache, }: LogtoProviderProps) => import("react/jsx-runtime.js").JSX.Element;
package/lib/provider.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import LogtoClient from '@logto/browser';
3
- import { useState, useMemo, useEffect } from 'react';
3
+ import { useState, useMemo, useCallback, useEffect } from 'react';
4
4
  import { LogtoContext } from './context.js';
5
5
 
6
6
  const LogtoProvider = ({ config, children, unstable_enableCache = false, }) => {
@@ -8,6 +8,15 @@ const LogtoProvider = ({ config, children, unstable_enableCache = false, }) => {
8
8
  const memorizedLogtoClient = useMemo(() => ({ logtoClient: new LogtoClient(config, unstable_enableCache) }), [config, unstable_enableCache]);
9
9
  const [isAuthenticated, setIsAuthenticated] = useState(false);
10
10
  const [error, setError] = useState();
11
+ const isLoading = useMemo(() => loadingCount > 0, [loadingCount]);
12
+ const setIsLoading = useCallback((state) => {
13
+ if (state) {
14
+ setLoadingCount((count) => count + 1);
15
+ }
16
+ else {
17
+ setLoadingCount((count) => Math.max(0, count - 1));
18
+ }
19
+ }, [setLoadingCount]);
11
20
  useEffect(() => {
12
21
  (async () => {
13
22
  const isAuthenticated = await memorizedLogtoClient.logtoClient.isAuthenticated();
@@ -19,11 +28,11 @@ const LogtoProvider = ({ config, children, unstable_enableCache = false, }) => {
19
28
  ...memorizedLogtoClient,
20
29
  isAuthenticated,
21
30
  setIsAuthenticated,
22
- loadingCount,
23
- setLoadingCount,
31
+ isLoading,
32
+ setIsLoading,
24
33
  error,
25
34
  setError,
26
- }), [memorizedLogtoClient, isAuthenticated, loadingCount, error]);
35
+ }), [memorizedLogtoClient, isAuthenticated, isLoading, setIsLoading, error]);
27
36
  return jsx(LogtoContext.Provider, { value: memorizedContextValue, children: children });
28
37
  };
29
38
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logto/react",
3
- "version": "2.2.4",
3
+ "version": "3.0.1",
4
4
  "type": "module",
5
5
  "main": "./lib/index.cjs",
6
6
  "module": "./lib/index.js",
@@ -21,8 +21,8 @@
21
21
  "directory": "packages/react"
22
22
  },
23
23
  "dependencies": {
24
- "@logto/browser": "^2.2.2",
25
- "@silverhand/essentials": "^2.8.7"
24
+ "@silverhand/essentials": "^2.8.7",
25
+ "@logto/browser": "^2.2.3"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@silverhand/eslint-config": "^5.0.0",
@@ -31,9 +31,9 @@
31
31
  "@silverhand/ts-config-react": "^5.0.0",
32
32
  "@swc/core": "^1.3.50",
33
33
  "@swc/jest": "^0.2.24",
34
- "@testing-library/react": "^14.0.0",
34
+ "@testing-library/react": "^14.2.1",
35
35
  "@types/jest": "^29.5.0",
36
- "@types/react": "^18.2.0",
36
+ "@types/react": "^18.2.56",
37
37
  "eslint": "^8.44.0",
38
38
  "jest": "^29.5.0",
39
39
  "lint-staged": "^15.0.0",
@@ -41,7 +41,7 @@
41
41
  "prettier": "^3.0.0",
42
42
  "react": "^18.0.2",
43
43
  "stylelint": "^16.0.0",
44
- "typescript": "^5.0.0"
44
+ "typescript": "^5.3.3"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "react": ">=16.8.0"