Skip to content
This repository has been archived by the owner on Jun 23, 2020. It is now read-only.

feat: improve types #12

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/context/auth0-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export interface IAuth0Context {
/**
* This method allows you to render a component while the user is being redirected to Auth0.
*/
onRedirecting?: () => React.Component;
onRedirecting?: () => React.ReactElement<any> | null;

/**
* This method will be called after the user has been signed in, allowing you to redirect them to some page.
Expand Down
2 changes: 1 addition & 1 deletion src/context/auth0-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export interface Auth0ProviderOptions {
/**
* This method allows you to render a component while the user is being redirected to Auth0.
*/
onRedirecting?: () => React.Component;
onRedirecting?: () => React.ReactElement<any> | null;

/**
* This method will be called after the user has been signed in, allowing you to redirect them to some page.
Expand Down
44 changes: 21 additions & 23 deletions src/utils/with-wrapper.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,44 @@
import {
ReactNode,
ReactChildren,
FunctionComponent,
ComponentClass
ComponentType,
ReactElement
} from 'react';

interface INextPage {
getInitialProps?(ctx: any): Promise<any>;
}

export interface IComponentProps {
[key: string]: any;
children: ReactChildren;
}

interface RenderWrapper<TProps extends IComponentProps> {
(props: TProps): ReactNode;
}

function getComponentName(ChildComponent: ComponentClass<any>): string {
function getComponentName<TProps extends {}>(
ChildComponent: ComponentType<TProps>
): string {
return ChildComponent.displayName || ChildComponent.name || 'Component';
}

function tryGetInitialPropsMethod(
child: ComponentClass<any>
function tryGetInitialPropsMethod<TProps extends {}>(
child: ComponentType<TProps>
): ((ctx: any) => Promise<any>) | undefined {
const nextPage = child as INextPage;
return nextPage && nextPage.getInitialProps;
}

export default function withWrapper<TPropsType extends IComponentProps>(
ChildComponent: ComponentClass<any>,
export default function withWrapper<
TPropsType extends {},
TChildProps extends {}
>(
ChildComponent: ComponentType<TChildProps>,
name: string,
render: RenderWrapper<TPropsType>
): ReactNode {
const WrappedComponent = (props: TPropsType): ReactNode => render(props);
(WrappedComponent as FunctionComponent).displayName = `${name}(${getComponentName(
ChildComponent
)})`;
render: (
props: TPropsType
) => ReactElement<TChildProps> | null
): ComponentType<TChildProps & TPropsType> {
const WrappedComponent: FunctionComponent<TChildProps & TPropsType> = (props) => render(props);

// eslint-disable-next-line no-param-reassign
WrappedComponent.displayName = `${name}(${getComponentName(ChildComponent)})`;

// Helper for Next.js support (getInitialProps)
const getInitialProps = tryGetInitialPropsMethod(ChildComponent);

if (getInitialProps) {
const WrappedComponentNext = WrappedComponent as INextPage;
WrappedComponentNext.getInitialProps = async (args: any): Promise<any> => getInitialProps(args);
Expand Down
13 changes: 6 additions & 7 deletions src/wrappers/with-auth.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import React from 'react';

import useAuth from '../hooks/use-auth';
import { AccessTokenRequestOptions } from '../context/auth0-context';
import withWrapper, { IComponentProps } from '../utils/with-wrapper';
import withWrapper from '../utils/with-wrapper';

export default function withAuth(
ChildComponent: React.ComponentClass<any>,
export default function withAuth<TChildProps>(
ChildComponent: React.ComponentType<TChildProps>,
options?: AccessTokenRequestOptions
): React.ReactNode {
return withWrapper<IComponentProps>(ChildComponent, 'withAuth', ({ ...props }) => {
): React.ComponentType<TChildProps> {
return withWrapper(ChildComponent, 'withAuth', ({ ...props }) => {
const auth = useAuth(options);

return (
<ChildComponent {...props} auth={auth} />
<ChildComponent {...props as TChildProps} auth={auth} />
);
});
}
53 changes: 32 additions & 21 deletions src/wrappers/with-login-required.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { parse } from 'query-string';
import React, { useEffect, useContext } from 'react';
import React, { useEffect, useContext, ReactElement } from 'react';

import useAuth from '../hooks/use-auth';
import Auth0Context from '../context/auth0-context';
import { ReturnToAppState } from '../models/return-to';
import withWrapper, { IComponentProps } from '../utils/with-wrapper';
import withWrapper from '../utils/with-wrapper';

export interface RequireLoginProps extends IComponentProps {
path: string;
export interface RequireLoginProps {
path?: string;
}

function getReturnTo(): ReturnToAppState {
Expand All @@ -20,25 +20,36 @@ function getReturnTo(): ReturnToAppState {
};
}

return { };
return {};
}

export default function withLoginRequired(ChildComponent: React.ComponentClass<any>): React.ReactNode {
return withWrapper<RequireLoginProps>(ChildComponent, 'withLoginRequired', ({ path, ...rest }) => {
const {
isLoading, isAuthenticated, login
} = useAuth();
const context = useContext(Auth0Context);

useEffect(() => {
if (!context.client || isLoading || isAuthenticated) {
return;
export default function withLoginRequired<T extends {}>(
ChildComponent: React.ComponentType<T>
): React.ComponentType<RequireLoginProps & T> {
return withWrapper<RequireLoginProps, T>(
ChildComponent,
'withLoginRequired',
({ path, ...rest }): ReactElement<any> | null => {
const { isLoading, isAuthenticated, login } = useAuth();
const context = useContext(Auth0Context);

useEffect(() => {
if (!context.client || isLoading || isAuthenticated) {
return;
}

login({ appState: getReturnTo() });
}, [context.client, isLoading, isAuthenticated, login, path]);

if (isAuthenticated) {
// cast to T needed https://github.com/Microsoft/TypeScript/issues/28938
return <ChildComponent {...(rest as T)} />;
}

login({ appState: getReturnTo() });
}, [context.client, isLoading, isAuthenticated, login, path]);

return isAuthenticated === true
? (<ChildComponent {...rest} />) : ((context.handlers.onRedirecting && context.handlers.onRedirecting()) || null);
});
return (
(context.handlers.onRedirecting && context.handlers.onRedirecting())
|| null
);
}
);
}