@stytch/react 0.0.0-rc.3 → 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,8 @@ 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
- export { Stytch, useStytchUser, useStytchSession, useStytch, withStytch, withStytchUser, withStytchSession, StytchProviderProps, StytchProvider };
34
+ export { Stytch, StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser };
35
+ export type { StytchProviderProps };
@@ -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,8 @@ 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
- export { Stytch, useStytchUser, useStytchSession, useStytch, withStytch, withStytchUser, withStytchSession, StytchProviderProps, StytchProvider };
34
+ export { Stytch, StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser };
35
+ export type { StytchProviderProps };
package/dist/index.esm.js CHANGED
@@ -1,43 +1,90 @@
1
1
  import * as React from 'react';
2
- import { StytchUIClient } from '@stytch/vanilla-js';
2
+ import React__default, { useRef, useState, useEffect, useCallback, createContext, useContext, useMemo } from 'react';
3
3
 
4
- const StytchUserContext = React.createContext(null);
5
- const StytchSessionContext = React.createContext(null);
6
- 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;
41
+ const isUIClient = (client) => {
42
+ return client.mount !== undefined;
43
+ };
7
44
  const useStytchUser = () => {
8
- return React.useContext(StytchUserContext);
45
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchUser'));
46
+ return useContext(StytchUserContext);
9
47
  };
10
48
  const useStytchSession = () => {
11
- return React.useContext(StytchSessionContext);
49
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchSession'));
50
+ return useContext(StytchSessionContext);
12
51
  };
13
52
  const useStytch = () => {
14
- return React.useContext(StytchContext);
53
+ const ctx = useContext(StytchContext);
54
+ invariant(ctx.isMounted, noProviderError('useStytch'));
55
+ return ctx.client;
15
56
  };
16
57
  const withStytch = (Component) => {
17
58
  const WithStytch = (props) => {
18
- return React.createElement(Component, { ...props, stytch: useStytch() });
59
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytch'));
60
+ return React__default.createElement(Component, { ...props, stytch: useStytch() });
19
61
  };
20
62
  WithStytch.displayName = `withStytch(${Component.displayName || Component.name || 'Component'})`;
21
63
  return WithStytch;
22
64
  };
23
65
  const withStytchUser = (Component) => {
24
66
  const WithStytchUser = (props) => {
25
- return React.createElement(Component, { ...props, stytchUser: useStytchUser() });
67
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytchUser'));
68
+ return React__default.createElement(Component, { ...props, stytchUser: useStytchUser() });
26
69
  };
27
70
  WithStytchUser.displayName = `withStytchUser(${Component.displayName || Component.name || 'Component'})`;
28
71
  return WithStytchUser;
29
72
  };
30
73
  const withStytchSession = (Component) => {
31
74
  const WithStytchSession = (props) => {
32
- return React.createElement(Component, { ...props, stytchSession: useStytchSession() });
75
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytchSession'));
76
+ return React__default.createElement(Component, { ...props, stytchSession: useStytchSession() });
33
77
  };
34
78
  WithStytchSession.displayName = `withStytchSession(${Component.displayName || Component.name || 'Component'})`;
35
79
  return WithStytchSession;
36
80
  };
37
81
  const StytchProvider = ({ stytch, children }) => {
38
- const [user, setUser] = React.useState(null);
39
- const [session, setSession] = React.useState(null);
40
- 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(() => {
41
88
  const unsubscribeUser = stytch.user.onChange((user) => setUser(user));
42
89
  const unsubscribeSession = stytch.session.onChange((session) => setSession(session));
43
90
  return () => {
@@ -45,45 +92,41 @@ const StytchProvider = ({ stytch, children }) => {
45
92
  unsubscribeSession();
46
93
  };
47
94
  }, [stytch, setUser, setSession]);
48
- return (React.createElement(StytchContext.Provider, { value: stytch },
49
- React.createElement(StytchUserContext.Provider, { value: session && user },
50
- 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))));
51
98
  };
52
99
 
53
100
  /**
54
101
  * Returns a unique element ID.
55
- * Client-side only - will return null for server-side
56
- * resolves: https://stytch.slack.com/archives/C015UDB4X33/p1641450131000800
57
- * 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
58
104
  */
59
105
  const useUniqueElementId = () => {
60
- const [elementId, setElementId] = React.useState(null);
61
- React.useEffect(() => {
106
+ return React.useMemo(() => {
62
107
  const randId = Math.floor(Math.random() * 1e6);
63
- setElementId(`stytch-magic-link-${randId}`);
108
+ return `stytch-magic-link-${randId}`;
64
109
  }, []);
65
- return elementId;
66
110
  };
67
111
  /**
68
112
  * Stytch JS React Component
69
113
  *
70
114
  * [Documentation](https://stytch.com/docs/javascript-sdk)
71
115
  */
72
- const Stytch = ({ config, styles }) => {
116
+ const Stytch = ({ config, styles, callbacks, }) => {
117
+ invariant(useIsMounted__INTERNAL(), noProviderError('<Stytch />'));
73
118
  const stytchClient = useStytch();
74
119
  const elementId = useUniqueElementId();
75
120
  React.useEffect(() => {
76
- if (!stytchClient || !elementId) {
121
+ if (!elementId) {
77
122
  return;
78
123
  }
79
- if (!(stytchClient instanceof StytchUIClient)) {
80
- throw Error(`Tried to create a Stytch Login UI element using the Stytch Headless SDK.
81
- You must use the UI SDK to use UI elements.
82
- Please make sure you are importing from @stytch/vanilla-js and not from the @stytch/vanilla-js/headless.
83
- `);
124
+ if (!isUIClient(stytchClient)) {
125
+ throw Error(noHeadlessClientError);
84
126
  }
85
127
  stytchClient.mount({
86
128
  config,
129
+ callbacks,
87
130
  elementId: `#${elementId}`,
88
131
  styles,
89
132
  });
package/dist/index.js CHANGED
@@ -3,7 +3,8 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var React = require('react');
6
- var vanillaJs = require('@stytch/vanilla-js');
6
+
7
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
7
8
 
8
9
  function _interopNamespace(e) {
9
10
  if (e && e.__esModule) return e;
@@ -23,45 +24,93 @@ function _interopNamespace(e) {
23
24
  return Object.freeze(n);
24
25
  }
25
26
 
27
+ var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
26
28
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
27
29
 
28
- const StytchUserContext = React__namespace.createContext(null);
29
- const StytchSessionContext = React__namespace.createContext(null);
30
- 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;
67
+ const isUIClient = (client) => {
68
+ return client.mount !== undefined;
69
+ };
31
70
  const useStytchUser = () => {
32
- return React__namespace.useContext(StytchUserContext);
71
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchUser'));
72
+ return React.useContext(StytchUserContext);
33
73
  };
34
74
  const useStytchSession = () => {
35
- return React__namespace.useContext(StytchSessionContext);
75
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchSession'));
76
+ return React.useContext(StytchSessionContext);
36
77
  };
37
78
  const useStytch = () => {
38
- return React__namespace.useContext(StytchContext);
79
+ const ctx = React.useContext(StytchContext);
80
+ invariant(ctx.isMounted, noProviderError('useStytch'));
81
+ return ctx.client;
39
82
  };
40
83
  const withStytch = (Component) => {
41
84
  const WithStytch = (props) => {
42
- return React__namespace.createElement(Component, { ...props, stytch: useStytch() });
85
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytch'));
86
+ return React__default["default"].createElement(Component, { ...props, stytch: useStytch() });
43
87
  };
44
88
  WithStytch.displayName = `withStytch(${Component.displayName || Component.name || 'Component'})`;
45
89
  return WithStytch;
46
90
  };
47
91
  const withStytchUser = (Component) => {
48
92
  const WithStytchUser = (props) => {
49
- return React__namespace.createElement(Component, { ...props, stytchUser: useStytchUser() });
93
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytchUser'));
94
+ return React__default["default"].createElement(Component, { ...props, stytchUser: useStytchUser() });
50
95
  };
51
96
  WithStytchUser.displayName = `withStytchUser(${Component.displayName || Component.name || 'Component'})`;
52
97
  return WithStytchUser;
53
98
  };
54
99
  const withStytchSession = (Component) => {
55
100
  const WithStytchSession = (props) => {
56
- return React__namespace.createElement(Component, { ...props, stytchSession: useStytchSession() });
101
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytchSession'));
102
+ return React__default["default"].createElement(Component, { ...props, stytchSession: useStytchSession() });
57
103
  };
58
104
  WithStytchSession.displayName = `withStytchSession(${Component.displayName || Component.name || 'Component'})`;
59
105
  return WithStytchSession;
60
106
  };
61
107
  const StytchProvider = ({ stytch, children }) => {
62
- const [user, setUser] = React__namespace.useState(null);
63
- const [session, setSession] = React__namespace.useState(null);
64
- 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(() => {
65
114
  const unsubscribeUser = stytch.user.onChange((user) => setUser(user));
66
115
  const unsubscribeSession = stytch.session.onChange((session) => setSession(session));
67
116
  return () => {
@@ -69,45 +118,41 @@ const StytchProvider = ({ stytch, children }) => {
69
118
  unsubscribeSession();
70
119
  };
71
120
  }, [stytch, setUser, setSession]);
72
- return (React__namespace.createElement(StytchContext.Provider, { value: stytch },
73
- React__namespace.createElement(StytchUserContext.Provider, { value: session && user },
74
- 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))));
75
124
  };
76
125
 
77
126
  /**
78
127
  * Returns a unique element ID.
79
- * Client-side only - will return null for server-side
80
- * resolves: https://stytch.slack.com/archives/C015UDB4X33/p1641450131000800
81
- * 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
82
130
  */
83
131
  const useUniqueElementId = () => {
84
- const [elementId, setElementId] = React__namespace.useState(null);
85
- React__namespace.useEffect(() => {
132
+ return React__namespace.useMemo(() => {
86
133
  const randId = Math.floor(Math.random() * 1e6);
87
- setElementId(`stytch-magic-link-${randId}`);
134
+ return `stytch-magic-link-${randId}`;
88
135
  }, []);
89
- return elementId;
90
136
  };
91
137
  /**
92
138
  * Stytch JS React Component
93
139
  *
94
140
  * [Documentation](https://stytch.com/docs/javascript-sdk)
95
141
  */
96
- const Stytch = ({ config, styles }) => {
142
+ const Stytch = ({ config, styles, callbacks, }) => {
143
+ invariant(useIsMounted__INTERNAL(), noProviderError('<Stytch />'));
97
144
  const stytchClient = useStytch();
98
145
  const elementId = useUniqueElementId();
99
146
  React__namespace.useEffect(() => {
100
- if (!stytchClient || !elementId) {
147
+ if (!elementId) {
101
148
  return;
102
149
  }
103
- if (!(stytchClient instanceof vanillaJs.StytchUIClient)) {
104
- throw Error(`Tried to create a Stytch Login UI element using the Stytch Headless SDK.
105
- You must use the UI SDK to use UI elements.
106
- Please make sure you are importing from @stytch/vanilla-js and not from the @stytch/vanilla-js/headless.
107
- `);
150
+ if (!isUIClient(stytchClient)) {
151
+ throw Error(noHeadlessClientError);
108
152
  }
109
153
  stytchClient.mount({
110
154
  config,
155
+ callbacks,
111
156
  elementId: `#${elementId}`,
112
157
  styles,
113
158
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stytch/react",
3
- "version": "0.0.0-rc.3",
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.0-rc.3",
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.0",
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.0"
32
+ "stableVersion": "0.0.2"
32
33
  }