jskrtsth
jskrtsth

Reputation: 151

Extending AWS Amplify Auth components in typescript

So I'm trying to extend the existing Auth components in AWS Amplify such as SignIn, SignUp, etc. and override the showComponent() function to return a custom form as detailed in this post: https://blog.kylegalbraith.com/2018/11/29/how-to-easily-customize-the-aws-amplify-authentication-ui/

I'm using typescript for my nextjs project and I'm getting the following error: when I try to throw the custom component under the Authenticator component:

[ts]
JSX element type 'CustomSignUp' is not a constructor function for JSX elements.
  Type 'CustomSignUp' is missing the following properties from type 'ElementClass': render, context, setState, forceUpdate, and 3 more.

Here's my _app.tsx:

import {SignUp} from 'aws-amplify-react/dist/Auth/SignUp';
class NewApp extends App {
    static async getInitialProps({Component, ctx}) {
        let pageProps = {};

        if (Component.getInitialProps) {
            pageProps = await Component.getInitialProps(ctx);
        }

        return { pageProps }
    }

    render() {
        const {props} = this as any
        const {Component, pageProps} = props

        return (
            <Container>
                <Authenticator hide={[SignUp]}>
                    <CustomSignUp/>
                    <Component {...pageProps}/>
                </Authenticator>
            </Container>
        )
    }
}

export default NewApp;

And the CustomSignUp.tsx is just something stupidly simple for demonstration purposes:

class CustomSignUp extends SignUp {
    _validAuthStates;
    constructor(props) {
        super(props);

        this._validAuthStates = ['signUp'];
    }

    showComponent(theme) {
        return(
            <div>
                Hi!
            </div>
        )
    }
}

export default CustomSignUp;

What's the way to do this correctly?

Upvotes: 1

Views: 2211

Answers (1)

Greg Brodzik
Greg Brodzik

Reputation: 1817

Per your request above I will provide more detail. As I mentioned in my comment, we found implementing and customizing the AWS Amplify components to be restrictive and difficult. We therefore decided to simply build out our own UI Components as we normally would, manage authentication globally with the Amplify HUB module and a Cognito Auth method helper class. Finally, we pushed Cognito user data down through our components with our own simple HOC.

To start, in a Component mounted when your app first loads, you can import Hub from aws-amplify to add any event listeners relevant to your app in the Component -- perhaps in the constructor -- including listeners to monitor auth state:

    Hub.listen("auth", data => {
      const { payload } = data;
      if (payload.event === "signOut") {
        props.navigation.navigate("SigninScreen");
      }
    }

You can listen/respond to auth changes throughout your app, even if the component in which you established the listeners unmounts.

Next, you can build a simple class with the various methods from the Auth module, again imported from aws-amplify, encapsulating functionality such as Auth.currentAuthenicatedUser, Auth.signUp, etc. With our own UI we simply attached/invoked the Cognito methods at the appropriate places and time.

If you decide to take this route, the last gap to fill is how to pass down the data from Auth's currentAuthenticatedUser method to your components (as Amplify's out of the box HOC would do). You can make your own HOC which fetches user data by Auth.currentAuthenticatedUser(), and pass the received data via props to any Component it wraps - fairly straightforward.

Because we were using graphql/Apollo, in our case we decided to use Apollo Client local resolvers to retrieve/pass Cognito user data. You can read more about Apollo Client local resolvers here here if you're interested.

Upvotes: 3

Related Questions