@stytch/react 0.0.2-0 → 0.0.3-0

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,4 +1,4 @@
1
- # Stytch-React
1
+ # @stytch/react
2
2
 
3
3
  Stytch's React Library
4
4
 
@@ -30,7 +30,7 @@ ReactDOM.render(
30
30
  // Now use Stytch in your components
31
31
  const App = () => {
32
32
  const stytchProps = {
33
- loginOrSignupView: {
33
+ config: {
34
34
  products: ['emailMagicLinks'],
35
35
  emailMagicLinksOptions: {
36
36
  loginRedirectURL: 'https://example.com/authenticate',
@@ -40,7 +40,7 @@ const App = () => {
40
40
  createUserAsPending: true,
41
41
  },
42
42
  },
43
- style: {
43
+ styles: {
44
44
  fontFamily: '"Helvetica New", Helvetica, sans-serif',
45
45
  width: '321px',
46
46
  primaryColor: '#0577CA',
@@ -54,12 +54,7 @@ const App = () => {
54
54
 
55
55
  return (
56
56
  <div id="login">
57
- <Stytch
58
- publicToken={stytchProps.publicToken}
59
- loginOrSignupView={stytchProps.loginOrSignupView}
60
- style={stytchProps.style}
61
- callbacks={stytchProps.callbacks}
62
- />
57
+ <Stytch config={stytchProps.loginOrSignupView} styles={stytchProps.style} callbacks={stytchProps.callbacks} />
63
58
  </div>
64
59
  );
65
60
  };
package/dist/index.d.ts CHANGED
@@ -1,15 +1,17 @@
1
1
  /// <reference types="react" />
2
- import { Config, StyleConfig, User, Session, StytchUIClient } from "@stytch/vanilla-js";
3
- import * as React from "react";
2
+ import { Callbacks, Config, StyleConfig, User, Session, StytchUIClient } from "@stytch/vanilla-js";
3
+ import React from "react";
4
+ import { ReactNode } from "react";
4
5
  import { StytchHeadlessClient } from "@stytch/vanilla-js/headless";
5
6
  /**
6
7
  * Stytch JS React Component
7
8
  *
8
9
  * [Documentation](https://stytch.com/docs/javascript-sdk)
9
10
  */
10
- declare const Stytch: ({ config, styles }: {
11
+ declare const Stytch: ({ config, styles, callbacks }: {
11
12
  config: Config;
12
13
  styles: StyleConfig;
14
+ callbacks?: Callbacks | undefined;
13
15
  }) => JSX.Element | null;
14
16
  type StytchClient = StytchUIClient | StytchHeadlessClient;
15
17
  declare const useStytchUser: () => User | null;
@@ -26,7 +28,7 @@ declare const withStytchSession: <T extends object>(Component: React.ComponentTy
26
28
  }>) => React.ComponentType<T>;
27
29
  type StytchProviderProps = {
28
30
  stytch: StytchClient;
29
- children?: React.ReactNode;
31
+ children?: ReactNode;
30
32
  };
31
33
  declare const StytchProvider: ({ stytch, children }: StytchProviderProps) => JSX.Element;
32
34
  export { Stytch, StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser };
@@ -1,15 +1,17 @@
1
1
  /// <reference types="react" />
2
- import { Config, StyleConfig, User, Session, StytchUIClient } from "@stytch/vanilla-js";
3
- import * as React from "react";
2
+ import { Callbacks, Config, StyleConfig, User, Session, StytchUIClient } from "@stytch/vanilla-js";
3
+ import React from "react";
4
+ import { ReactNode } from "react";
4
5
  import { StytchHeadlessClient } from "@stytch/vanilla-js/headless";
5
6
  /**
6
7
  * Stytch JS React Component
7
8
  *
8
9
  * [Documentation](https://stytch.com/docs/javascript-sdk)
9
10
  */
10
- declare const Stytch: ({ config, styles }: {
11
+ declare const Stytch: ({ config, styles, callbacks }: {
11
12
  config: Config;
12
13
  styles: StyleConfig;
14
+ callbacks?: Callbacks | undefined;
13
15
  }) => JSX.Element | null;
14
16
  type StytchClient = StytchUIClient | StytchHeadlessClient;
15
17
  declare const useStytchUser: () => User | null;
@@ -26,7 +28,7 @@ declare const withStytchSession: <T extends object>(Component: React.ComponentTy
26
28
  }>) => React.ComponentType<T>;
27
29
  type StytchProviderProps = {
28
30
  stytch: StytchClient;
29
- children?: React.ReactNode;
31
+ children?: ReactNode;
30
32
  };
31
33
  declare const StytchProvider: ({ stytch, children }: StytchProviderProps) => JSX.Element;
32
34
  export { Stytch, StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser };
package/dist/index.esm.js CHANGED
@@ -1,45 +1,90 @@
1
1
  import * as React from 'react';
2
+ import React__default, { useRef, useState, useEffect, useCallback, createContext, useContext, useMemo } from 'react';
2
3
 
3
- const StytchUserContext = React.createContext(null);
4
- const StytchSessionContext = React.createContext(null);
5
- const StytchContext = React.createContext({});
4
+ const noProviderError = (item) => `${item} can only be used inside <StytchProvider>.`;
5
+ const providerMustBeUniqueError = 'You cannot render a <StytchProvider> inside another <StytchProvider>.';
6
+ const noSSRError = `The @stytch/react library is not meant for use with serverside environments like NextJS.
7
+ Use the @stytch/nextjs library instead -
8
+ npm remove @stytch/react && npm install @stytch/nextjs
9
+ `;
10
+ const noHeadlessClientError = `Tried to create a Stytch Login UI element using the Stytch Headless SDK.
11
+ You must use the UI SDK to use UI elements.
12
+ Please make sure you are importing from @stytch/vanilla-js and not from the @stytch/vanilla-js/headless.`;
13
+
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ function invariant(cond, message) {
16
+ if (!cond)
17
+ throw new Error(message);
18
+ }
19
+
20
+ // useState can cause memory leaks if it is set after the component unmounted. For example, if it is
21
+ // set after `await`, or in a `then`, `catch`, or `finally`, or in a setTimout/setInterval.
22
+ const useAsyncState = (initialState) => {
23
+ const isMounted = useRef(true);
24
+ const [state, setState] = useState(initialState);
25
+ useEffect(() => {
26
+ isMounted.current = true;
27
+ return () => {
28
+ isMounted.current = false;
29
+ };
30
+ }, []);
31
+ const setStateAction = useCallback((newState) => {
32
+ isMounted.current && setState(newState);
33
+ }, []);
34
+ return [state, setStateAction];
35
+ };
36
+
37
+ const StytchContext = createContext({ isMounted: false });
38
+ const StytchUserContext = createContext(null);
39
+ const StytchSessionContext = createContext(null);
40
+ const useIsMounted__INTERNAL = () => useContext(StytchContext).isMounted;
6
41
  const isUIClient = (client) => {
7
42
  return client.mount !== undefined;
8
43
  };
9
44
  const useStytchUser = () => {
10
- return React.useContext(StytchUserContext);
45
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchUser'));
46
+ return useContext(StytchUserContext);
11
47
  };
12
48
  const useStytchSession = () => {
13
- return React.useContext(StytchSessionContext);
49
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchSession'));
50
+ return useContext(StytchSessionContext);
14
51
  };
15
52
  const useStytch = () => {
16
- return React.useContext(StytchContext);
53
+ const ctx = useContext(StytchContext);
54
+ invariant(ctx.isMounted, noProviderError('useStytch'));
55
+ return ctx.client;
17
56
  };
18
57
  const withStytch = (Component) => {
19
58
  const WithStytch = (props) => {
20
- return React.createElement(Component, { ...props, stytch: useStytch() });
59
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytch'));
60
+ return React__default.createElement(Component, { ...props, stytch: useStytch() });
21
61
  };
22
62
  WithStytch.displayName = `withStytch(${Component.displayName || Component.name || 'Component'})`;
23
63
  return WithStytch;
24
64
  };
25
65
  const withStytchUser = (Component) => {
26
66
  const WithStytchUser = (props) => {
27
- return React.createElement(Component, { ...props, stytchUser: useStytchUser() });
67
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytchUser'));
68
+ return React__default.createElement(Component, { ...props, stytchUser: useStytchUser() });
28
69
  };
29
70
  WithStytchUser.displayName = `withStytchUser(${Component.displayName || Component.name || 'Component'})`;
30
71
  return WithStytchUser;
31
72
  };
32
73
  const withStytchSession = (Component) => {
33
74
  const WithStytchSession = (props) => {
34
- return React.createElement(Component, { ...props, stytchSession: useStytchSession() });
75
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytchSession'));
76
+ return React__default.createElement(Component, { ...props, stytchSession: useStytchSession() });
35
77
  };
36
78
  WithStytchSession.displayName = `withStytchSession(${Component.displayName || Component.name || 'Component'})`;
37
79
  return WithStytchSession;
38
80
  };
39
81
  const StytchProvider = ({ stytch, children }) => {
40
- const [user, setUser] = React.useState(null);
41
- const [session, setSession] = React.useState(null);
42
- React.useEffect(() => {
82
+ invariant(!useIsMounted__INTERNAL(), providerMustBeUniqueError);
83
+ invariant(typeof window !== 'undefined', noSSRError);
84
+ const ctx = useMemo(() => ({ client: stytch, isMounted: true }), [stytch]);
85
+ const [user, setUser] = useAsyncState(stytch.user.getSync());
86
+ const [session, setSession] = useAsyncState(stytch.session.getSync());
87
+ useEffect(() => {
43
88
  const unsubscribeUser = stytch.user.onChange((user) => setUser(user));
44
89
  const unsubscribeSession = stytch.session.onChange((session) => setSession(session));
45
90
  return () => {
@@ -47,45 +92,41 @@ const StytchProvider = ({ stytch, children }) => {
47
92
  unsubscribeSession();
48
93
  };
49
94
  }, [stytch, setUser, setSession]);
50
- return (React.createElement(StytchContext.Provider, { value: stytch },
51
- React.createElement(StytchUserContext.Provider, { value: session && user },
52
- React.createElement(StytchSessionContext.Provider, { value: user && session }, children))));
95
+ return (React__default.createElement(StytchContext.Provider, { value: ctx },
96
+ React__default.createElement(StytchUserContext.Provider, { value: session && user },
97
+ React__default.createElement(StytchSessionContext.Provider, { value: user && session }, children))));
53
98
  };
54
99
 
55
100
  /**
56
101
  * Returns a unique element ID.
57
- * Client-side only - will return null for server-side
58
- * resolves: https://stytch.slack.com/archives/C015UDB4X33/p1641450131000800
59
- * based on: https://github.com/vercel/next.js/issues/7322#issuecomment-968858477
102
+ * Intended to only be used client-side - this will cause HTML mismatch issues in SSR!
103
+ * cf: https://github.com/vercel/next.js/issues/7322#issuecomment-968858477
60
104
  */
61
105
  const useUniqueElementId = () => {
62
- const [elementId, setElementId] = React.useState(null);
63
- React.useEffect(() => {
106
+ return React.useMemo(() => {
64
107
  const randId = Math.floor(Math.random() * 1e6);
65
- setElementId(`stytch-magic-link-${randId}`);
108
+ return `stytch-magic-link-${randId}`;
66
109
  }, []);
67
- return elementId;
68
110
  };
69
111
  /**
70
112
  * Stytch JS React Component
71
113
  *
72
114
  * [Documentation](https://stytch.com/docs/javascript-sdk)
73
115
  */
74
- const Stytch = ({ config, styles }) => {
116
+ const Stytch = ({ config, styles, callbacks, }) => {
117
+ invariant(useIsMounted__INTERNAL(), noProviderError('<Stytch />'));
75
118
  const stytchClient = useStytch();
76
119
  const elementId = useUniqueElementId();
77
120
  React.useEffect(() => {
78
- if (!stytchClient || !elementId) {
121
+ if (!elementId) {
79
122
  return;
80
123
  }
81
124
  if (!isUIClient(stytchClient)) {
82
- throw Error(`Tried to create a Stytch Login UI element using the Stytch Headless SDK.
83
- You must use the UI SDK to use UI elements.
84
- Please make sure you are importing from @stytch/vanilla-js and not from the @stytch/vanilla-js/headless.
85
- `);
125
+ throw Error(noHeadlessClientError);
86
126
  }
87
127
  stytchClient.mount({
88
128
  config,
129
+ callbacks,
89
130
  elementId: `#${elementId}`,
90
131
  styles,
91
132
  });
package/dist/index.js CHANGED
@@ -4,6 +4,8 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var React = require('react');
6
6
 
7
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
+
7
9
  function _interopNamespace(e) {
8
10
  if (e && e.__esModule) return e;
9
11
  var n = Object.create(null);
@@ -22,48 +24,93 @@ function _interopNamespace(e) {
22
24
  return Object.freeze(n);
23
25
  }
24
26
 
27
+ var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
25
28
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
26
29
 
27
- const StytchUserContext = React__namespace.createContext(null);
28
- const StytchSessionContext = React__namespace.createContext(null);
29
- const StytchContext = React__namespace.createContext({});
30
+ const noProviderError = (item) => `${item} can only be used inside <StytchProvider>.`;
31
+ const providerMustBeUniqueError = 'You cannot render a <StytchProvider> inside another <StytchProvider>.';
32
+ const noSSRError = `The @stytch/react library is not meant for use with serverside environments like NextJS.
33
+ Use the @stytch/nextjs library instead -
34
+ npm remove @stytch/react && npm install @stytch/nextjs
35
+ `;
36
+ const noHeadlessClientError = `Tried to create a Stytch Login UI element using the Stytch Headless SDK.
37
+ You must use the UI SDK to use UI elements.
38
+ Please make sure you are importing from @stytch/vanilla-js and not from the @stytch/vanilla-js/headless.`;
39
+
40
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
+ function invariant(cond, message) {
42
+ if (!cond)
43
+ throw new Error(message);
44
+ }
45
+
46
+ // useState can cause memory leaks if it is set after the component unmounted. For example, if it is
47
+ // set after `await`, or in a `then`, `catch`, or `finally`, or in a setTimout/setInterval.
48
+ const useAsyncState = (initialState) => {
49
+ const isMounted = React.useRef(true);
50
+ const [state, setState] = React.useState(initialState);
51
+ React.useEffect(() => {
52
+ isMounted.current = true;
53
+ return () => {
54
+ isMounted.current = false;
55
+ };
56
+ }, []);
57
+ const setStateAction = React.useCallback((newState) => {
58
+ isMounted.current && setState(newState);
59
+ }, []);
60
+ return [state, setStateAction];
61
+ };
62
+
63
+ const StytchContext = React.createContext({ isMounted: false });
64
+ const StytchUserContext = React.createContext(null);
65
+ const StytchSessionContext = React.createContext(null);
66
+ const useIsMounted__INTERNAL = () => React.useContext(StytchContext).isMounted;
30
67
  const isUIClient = (client) => {
31
68
  return client.mount !== undefined;
32
69
  };
33
70
  const useStytchUser = () => {
34
- return React__namespace.useContext(StytchUserContext);
71
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchUser'));
72
+ return React.useContext(StytchUserContext);
35
73
  };
36
74
  const useStytchSession = () => {
37
- return React__namespace.useContext(StytchSessionContext);
75
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchSession'));
76
+ return React.useContext(StytchSessionContext);
38
77
  };
39
78
  const useStytch = () => {
40
- return React__namespace.useContext(StytchContext);
79
+ const ctx = React.useContext(StytchContext);
80
+ invariant(ctx.isMounted, noProviderError('useStytch'));
81
+ return ctx.client;
41
82
  };
42
83
  const withStytch = (Component) => {
43
84
  const WithStytch = (props) => {
44
- return React__namespace.createElement(Component, { ...props, stytch: useStytch() });
85
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytch'));
86
+ return React__default["default"].createElement(Component, { ...props, stytch: useStytch() });
45
87
  };
46
88
  WithStytch.displayName = `withStytch(${Component.displayName || Component.name || 'Component'})`;
47
89
  return WithStytch;
48
90
  };
49
91
  const withStytchUser = (Component) => {
50
92
  const WithStytchUser = (props) => {
51
- return React__namespace.createElement(Component, { ...props, stytchUser: useStytchUser() });
93
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytchUser'));
94
+ return React__default["default"].createElement(Component, { ...props, stytchUser: useStytchUser() });
52
95
  };
53
96
  WithStytchUser.displayName = `withStytchUser(${Component.displayName || Component.name || 'Component'})`;
54
97
  return WithStytchUser;
55
98
  };
56
99
  const withStytchSession = (Component) => {
57
100
  const WithStytchSession = (props) => {
58
- return React__namespace.createElement(Component, { ...props, stytchSession: useStytchSession() });
101
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytchSession'));
102
+ return React__default["default"].createElement(Component, { ...props, stytchSession: useStytchSession() });
59
103
  };
60
104
  WithStytchSession.displayName = `withStytchSession(${Component.displayName || Component.name || 'Component'})`;
61
105
  return WithStytchSession;
62
106
  };
63
107
  const StytchProvider = ({ stytch, children }) => {
64
- const [user, setUser] = React__namespace.useState(null);
65
- const [session, setSession] = React__namespace.useState(null);
66
- React__namespace.useEffect(() => {
108
+ invariant(!useIsMounted__INTERNAL(), providerMustBeUniqueError);
109
+ invariant(typeof window !== 'undefined', noSSRError);
110
+ const ctx = React.useMemo(() => ({ client: stytch, isMounted: true }), [stytch]);
111
+ const [user, setUser] = useAsyncState(stytch.user.getSync());
112
+ const [session, setSession] = useAsyncState(stytch.session.getSync());
113
+ React.useEffect(() => {
67
114
  const unsubscribeUser = stytch.user.onChange((user) => setUser(user));
68
115
  const unsubscribeSession = stytch.session.onChange((session) => setSession(session));
69
116
  return () => {
@@ -71,45 +118,41 @@ const StytchProvider = ({ stytch, children }) => {
71
118
  unsubscribeSession();
72
119
  };
73
120
  }, [stytch, setUser, setSession]);
74
- return (React__namespace.createElement(StytchContext.Provider, { value: stytch },
75
- React__namespace.createElement(StytchUserContext.Provider, { value: session && user },
76
- React__namespace.createElement(StytchSessionContext.Provider, { value: user && session }, children))));
121
+ return (React__default["default"].createElement(StytchContext.Provider, { value: ctx },
122
+ React__default["default"].createElement(StytchUserContext.Provider, { value: session && user },
123
+ React__default["default"].createElement(StytchSessionContext.Provider, { value: user && session }, children))));
77
124
  };
78
125
 
79
126
  /**
80
127
  * Returns a unique element ID.
81
- * Client-side only - will return null for server-side
82
- * resolves: https://stytch.slack.com/archives/C015UDB4X33/p1641450131000800
83
- * based on: https://github.com/vercel/next.js/issues/7322#issuecomment-968858477
128
+ * Intended to only be used client-side - this will cause HTML mismatch issues in SSR!
129
+ * cf: https://github.com/vercel/next.js/issues/7322#issuecomment-968858477
84
130
  */
85
131
  const useUniqueElementId = () => {
86
- const [elementId, setElementId] = React__namespace.useState(null);
87
- React__namespace.useEffect(() => {
132
+ return React__namespace.useMemo(() => {
88
133
  const randId = Math.floor(Math.random() * 1e6);
89
- setElementId(`stytch-magic-link-${randId}`);
134
+ return `stytch-magic-link-${randId}`;
90
135
  }, []);
91
- return elementId;
92
136
  };
93
137
  /**
94
138
  * Stytch JS React Component
95
139
  *
96
140
  * [Documentation](https://stytch.com/docs/javascript-sdk)
97
141
  */
98
- const Stytch = ({ config, styles }) => {
142
+ const Stytch = ({ config, styles, callbacks, }) => {
143
+ invariant(useIsMounted__INTERNAL(), noProviderError('<Stytch />'));
99
144
  const stytchClient = useStytch();
100
145
  const elementId = useUniqueElementId();
101
146
  React__namespace.useEffect(() => {
102
- if (!stytchClient || !elementId) {
147
+ if (!elementId) {
103
148
  return;
104
149
  }
105
150
  if (!isUIClient(stytchClient)) {
106
- throw Error(`Tried to create a Stytch Login UI element using the Stytch Headless SDK.
107
- You must use the UI SDK to use UI elements.
108
- Please make sure you are importing from @stytch/vanilla-js and not from the @stytch/vanilla-js/headless.
109
- `);
151
+ throw Error(noHeadlessClientError);
110
152
  }
111
153
  stytchClient.mount({
112
154
  config,
155
+ callbacks,
113
156
  elementId: `#${elementId}`,
114
157
  styles,
115
158
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stytch/react",
3
- "version": "0.0.2-0",
3
+ "version": "0.0.3-0",
4
4
  "description": "Stytch's official React Library",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.esm.js",
@@ -12,21 +12,22 @@
12
12
  "build": "npm run clean && npm run compile",
13
13
  "clean": "rm -rf ./dist",
14
14
  "compile": "rollup -c",
15
- "dev": "tsc -p tsconfig.build.json --watch"
15
+ "dev": "tsc -p tsconfig.build.json --watch",
16
+ "test": "jest"
16
17
  },
17
18
  "license": "MIT",
18
19
  "author": "Stytch",
19
20
  "devDependencies": {
20
21
  "@babel/runtime": "^7.18.6",
21
- "@stytch/vanilla-js": "0.0.1",
22
+ "@stytch/vanilla-js": "0.0.2-0",
22
23
  "eslint-config-custom": "0.0.0",
23
24
  "rollup": "^2.56.3",
24
25
  "typescript": "^4.5.3"
25
26
  },
26
27
  "peerDependencies": {
27
- "@stytch/vanilla-js": "^0.0.1",
28
+ "@stytch/vanilla-js": "^0.0.2-0",
28
29
  "react": ">= 17.0.2",
29
30
  "react-dom": ">= 17.0.2"
30
31
  },
31
- "stableVersion": "0.0.1"
32
+ "stableVersion": "0.0.2"
32
33
  }