Amplify-js: How to use ApolloProvider with withAuthenticator HOC

Created on 18 Feb 2019  路  11Comments  路  Source: aws-amplify/amplify-js

* Which Category is your question related to? *
Auth, API, aws-amplify-react HoCs

* What AWS Services are you utilizing? *
AppSync, Cognito, Amplify

* Provide additional details e.g. code snippets *
When I try to add the ApolloProvider to my React-based Amplify project, I am met with a slew of errors while trying to compose withAuthenticator with my own Higher Order Component.

This is probably a noob question, sorry if I'm doing something obviously wrong. (I assume a HoC can nest multiple layers?)

//App.js
/*** class App extends React.Component { ... } ***/ 

const withAppSyncProvider = (Comp) => {
  return class extends React.Component {
    render() {
      return (
      <ApolloProvider client={client}>
        <Rehydrated>
          <Comp />
        </Rehydrated>
      </ApolloProvider>
      );
    }
  }
}


export default withAuthenticator(withAppSyncProvider(App)); 

That code meets with this error:

index.js:1446 Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check your code at App.js:103.
    in _class (created by class_1)
    in class_1 (at src/index.js:12)
GraphQL React pending-close-response-required

All 11 comments

@haverchuck this issue persists when attempting to just use actual Authenticator component:

const withAppSyncProvider = (Comp) => {
  return class extends React.Component {
    render() {
      return (
        <Authenticator>
          <ApolloProvider client={client}>
            <Rehydrated>
              <Comp />
            </Rehydrated>
          </ApolloProvider>
        </Authenticator>
      );
    }
  }
}


export default withAppSyncProvider(App); 

I assume that we want Authenticator to wrap ApolloProvider, and not vice-versa, because the user needs to be logged in before they can make queries.

Is there any way to unblock myself here without writing my own authenticator components?

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';

const root = document.getElementById('root');

const render = (Component) => {
  ReactDOM.render(
      <Component />,
    root
  );
};

render(App); 

@jkeys-ecg-nmsu - I was able to get the code from your original post working. However, I reproduced the error you cited by not importing the <ApolloProvider> and <Rehydrated> properly.

Please make sure you are doing:

import { ApolloProvider } from 'react-apollo';
import { Rehydrated } from 'aws-appsync-react';

Does that fix your issue?

@haverchuck Unfortunately no, here are what my imports look like. I did make sure I was using named exports where appropriate.

import React from 'react';
import { withAuthenticator, Authenticator, Rehydrated } from 'aws-amplify-react';
import { ApolloProvider, withApollo } from 'react-apollo';

I bootstrapped my application with create-react-app. Could you share your webpack.config.js?

@haverchuck I will start from scratch without create-react-app and see if I can get past this problem. Ty for letting me know that my code works. I'd like to leave this issue open until I've identified the problem (I'm leaning heavily towards webpack atm), if you don't mind.

@jkeys-ecg-nmsu - We're happy to leave this open! Please just keep us posted.

For what it's worth, I used create-react-app as well on my working sample.

@haverchuck I started from scratch with a new create-react-app application and a new Amplify backend. Here are my index.js and
index.js (unchanged from create-react-app default):

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

App.js:

import React, { Component } from 'react';
import { ApolloProvider } from 'react-apollo';
import Amplify, { Auth, Hub } from 'aws-amplify';
import { withAuthenticator, Rehydrated } from 'aws-amplify-react';
import AWSAppSyncClient from 'aws-appsync';
import aws_exports from './aws-exports.js';
import { AUTH_TYPE } from "aws-appsync/lib/link/auth-link";
import './App.css';

Amplify.configure(aws_exports);

const client = new AWSAppSyncClient({
    url: aws_exports.aws_appsync_graphqlEndpoint,
    region: aws_exports.aws_appsync_region,
    auth: {
        type: AUTH_TYPE.AMAZON_COGNITO_USER_POOLS,
        jwtToken: async () => (await Auth.currentSession()).getAccessToken().getJwtToken() 
    }
})

class App extends Component {
  render() {
    return (
      <div className="App">
        Hello
      </div>
    );
  }
}

const WithAppSyncProvider = (Comp) => {
  return class extends Component {
    constructor(props) {
      super(props);

      this.state = {}
    }

    async componentDidMount() {
        this.setState({token: async () => (await Auth.currentSession()).getAccessToken().getJwtToken()});
    }

    render() {


      return (
          <ApolloProvider client={client} >
            <Rehydrated>
              <Comp />
            </Rehydrated>
          </ApolloProvider>
      );
    }
  }
}

export default withAuthenticator(WithAppSyncProvider(App));

As far as I can tell, this is the minimal set of imports required to hook up the AWSAppSyncClient with the ApolloProvider. But, I'm still getting this error:

index.js:1446 Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports. Check your code at App.js:48.

The only thing I can think of, at this point, is that our systems are fundamentally different. I am building my project on WSL (Ubuntu) for Windows; what environment are you building in, standalone Linux? Powershell?

This is driving me crazy, so any clues would be much appreciated.

@jkeys-ecg-nmsu - When I use your import statements, I can reproduce the error.

I am pretty sure that the issue is that you're trying to import Rehydrated from aws-amplify-react. Trying importing it from 'aws-appsync-react'.

@haverchuck we figured this out at roughly the same time as you :P What a stupid mistake to make. Sorry for wasting your time.

@haverchuck I just want to circle back and reflect that your original response, if I had read it carefully, I would have seen that it contained the answer. I made an assumption that wasted both our time. Lesson learned (hopefully) =)

@jkeys-ecg-nmsu I'm trying to set up something similar, using only Amplify's authentication components and just AppSync for the rest of it. I'm almost there, but I'm getting an auth error when I submit a valid username and password. Out of curiosity, which auth method(s) did you enable in your AppSync API?

Never mind. Looks like my aws_exports was just missing a few attributes.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

leantide picture leantide  路  3Comments

rayhaanq picture rayhaanq  路  3Comments

romainquellec picture romainquellec  路  3Comments

ldgarcia picture ldgarcia  路  3Comments

callmekatootie picture callmekatootie  路  3Comments