Malpaux / apollo-offline

An offline toolkit for the Apollo client
https://www.npmjs.com/package/apollo-offline
BSD 3-Clause "New" or "Revised" License
188 stars 8 forks source link

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. #1

Closed TheoMer closed 7 years ago

TheoMer commented 7 years ago

@MLPXBrachmann I'm still cutting my teeth on the React ecosystem, and am getting the above mentioned error when doing the following:

Rehyrdated.js

import React from 'react';
import { connect } from 'react-redux';

export const Rehydrated = connect(({ rehydrated }) => ({ rehydrated }))
((props) => props.rehydrate ? props.children : props.loading);

app.js

import Rehydrated from './components/Rehydrated';

      <ApolloProvider store={store} client={client}>
        <Rehydrated>
            <App>
        </Rehydrated>
      </ApolloProvider>

Can you tell me what exactly I'm doing incorrectly in Rehyrdated.js and what specifically { rehydrated } is supposed to reference if any?

Many thanks in advance

PaulBrachmann commented 7 years ago

@TheoMer Don't worry, React can indeed be quite confusing at first.

The error you are getting basically tells you React is trying to render something that does not exist. In this case it may be the loading indicator (props.loading in Rehydrated.js). You need to either pass a React Element for app.js or add it to the Rehydrated component's defaultProps.

The updated code would look something like this:

components/Rehydrated.js

import React from 'react';
import { connect } from 'react-redux';

+ const Rehydrated = (props) => props.rehydrated ? props.children : props.loading;
+ 
+ // If you decide to not create a component to display while loading, use 'defaultProps'
+ Rehydrated.defaultProps = {
+   loading: false,
+ };
+ 
+ // Use 'export default' to declare a module's main export
- export const Rehydrated = connect(({ rehydrated }) => ({ rehydrated }))
- ((props) => props.rehydrate ? props.children : props.loading);
+ export default connect(({ rehydrated }) => ({ rehydrated }))(Rehydrated);

app.js

+ import offline from 'apollo-offline';
+ import React from 'react';
+ import { ApolloClient, ApolloProvider, createNetworkInterface } from 'react-apollo';
+ import ReactDOM from 'react-dom';
+ import { applyMiddleware, combineReducers, compose, createStore } from 'redux';
+ import config from 'redux-offline/lib/defaults';
+
+ import App from './App';
+ // Now you can use the default export
import Rehydrated from './components/Rehydrated';

+ /* Your setup goes here (see apollo-offline's README.md) */
+
+ /** Component to display while loading (if you choose to use one) */
+ const Loading = () => <div> Loading... </div>;
+ 
+ ReactDOM.render(
  <ApolloProvider client={client} store={store}>
-     <Rehydrated>
+     <Rehydrated loading={<Loading />}>
-       <App>
+       <App />
    </Rehydrated>
  </ApolloProvider>
+   document.getElementById('root'),
+ );

You do not have to specify Rehydrated.defaultProps and pass a loading element (although you may do both) - doing one should be enough to fix the error.

The { rehydrated } construct is a destructuring assignment that is used to map the Redux store's rehydrated field to the Rehydrated component's props (props.rehydrated).

The following code would be equivalent: components/Rehydrated.js

/* Code from above... */

export default connect((state) => {
  return { rehydrated: state.rehydrated };
})(Rehydrated);

I hope this helps you solve your problem. If the error persists or you have any further questions, feel free to just comment again!