import { CallbackOptions, loadStytch, LoginOrSignupView, StyleConfig, StytchClient } from '@stytch/stytch-js'; import React, { useEffect, useState } from 'react'; import { useAsyncState } from 'utils/async'; import { invariant } from 'utils/invariant'; import { useIsMounted__INTERNAL, useStytchRaw__INTERNAL } from './StytchContext'; export interface StytchProps { publicToken?: string; loginOrSignupView?: LoginOrSignupView; style?: StyleConfig; callbacks?: CallbackOptions; } /** * Returns a unique element ID. * Client-side only - will return null for server-side * resolves: https://stytch.slack.com/archives/C015UDB4X33/p1641450131000800 * based on: https://github.com/vercel/next.js/issues/7322#issuecomment-968858477 */ const useUniqueElementId = (): string | null => { const [elementId, setElementId] = useState(null); useEffect(() => { const randId = Math.floor(Math.random() * 1e6); setElementId(`stytch-magic-link-${randId}`); }, []); return elementId; }; /** * Stytch JS React Component * * [Documentation](https://stytch.com/docs/javascript-sdk) */ export const Stytch = ({ publicToken, style, callbacks, loginOrSignupView }: StytchProps) => { const stytchClientFromContext = useStytchRaw__INTERNAL(); const stytchMounted = useIsMounted__INTERNAL(); const [stytchClient, setStytchClient] = useAsyncState(() => { // If StytchProvider has been mounted, use context value if (stytchMounted) { return stytchClientFromContext; } // If StytchProvider isn't used, publicToken must be provided invariant( publicToken, 'The Stytch component must either be inside a or provided the publicToken prop.', ); // If Stytch has already been loaded, use global value if (typeof window !== undefined && window.Stytch) { return window.Stytch(publicToken); } // Otherwise, we will load Stytch return null; }); const elementId = useUniqueElementId(); useEffect(() => { if (!stytchClient) { if (!stytchMounted) { loadStytch().then((globalStytch) => globalStytch && setStytchClient(globalStytch(publicToken))); } else if (stytchClientFromContext) { setStytchClient(stytchClientFromContext); } } }, [stytchClient, stytchClientFromContext, stytchMounted, publicToken, setStytchClient]); useEffect(() => { if (!stytchClient || !elementId) { return; } stytchClient.mount({ callbacks, elementId: `#${elementId}`, loginOrSignupView, style, }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [elementId, stytchClient]); return elementId ?
: null; };