Next.js: React memo is not considered a valid react component

Created on 16 Nov 2018  路  8Comments  路  Source: vercel/next.js

Bug report

React.memo for page components

Describe the bug

When using React.memo as the exported component in a page, Next JS throws an error :
The default export is not a React Component in page: "/"

To Reproduce

You can check the behaviour here :
https://codesandbox.io/s/k2jx562o3

Expected behavior

The memo component should be considered a valid component

Screenshots

capture d ecran 2018-11-16 a 21 41 55

System information

  • Version of Next.js: [7.0.2]

Additional context

When adding memo to the component while hot reloading is active, it doesn't throw the error.
In that case, the error occurs only on first load, or when we reload the page.

Probably because of this line
https://github.com/zeit/next.js/blob/b63dda7cf7e9a58c3687fb8007c1d899b692584d/packages/next-server/server/render.js#L86

Switching to react-is would probably solve it and make this condition future-proof

Most helpful comment

@realtebo in your case the issue is you are returning the React element, not a React component.

Change your lib/layout.js to

import Header from '../components/header'

const layoutStyle = {
  margin: 20,
  padding: 20,
  border: '1px solid #DDD'
}

const withLayout = Component => (props) => (
  <div style={layoutStyle}>
    <Header />
    <Component />
  </div>
)

export default withLayout

Or something like that. what's happening is your are using a normal component as a High Order Component, the correct syntax of a HOC is WrappedComponent => NewComponent where your NextComponent (in your case is withLayout) can use the WrappedComponent (your page) internally.

All 8 comments

I was just writing the same issues !

I faced same identical problem using no other than the code in the learn section.

I am surprised becaused I've written the 'learn' section code about ten times and it's the first time I got this problem.

/pages/index.js

import withLayout from '../lib/layout'

const Page = () => (
  <p>This is the HOME page</p>
)

export default withLayout(Page)

/lib/layout

import Header from '../components/header'

const layoutStyle = {
  margin: 20,
  padding: 20,
  border: '1px solid #DDD'
}

const Layout = (props) => (
  <div style={layoutStyle}>
    <Header />
    {props.children}
  </div>
)

export default Layout

The error

The default export is not a React Component in page: "/index"
Error: The default export is not a React Component in page: "/index"
    at _callee3$ (E:\next_js\agenda\node_modules\next\dist\server\render.js:242:19)
    at tryCatch (E:\next_js\agenda\node_modules\regenerator-runtime\runtime.js:62:40)
    at Generator.invoke [as _invoke] (E:\next_js\agenda\node_modules\regenerator-runtime\runtime.js:288:22)
    at Generator.prototype.(anonymous function) [as next] (E:\next_js\agenda\node_modules\regenerator-runtime\runtime.js:114:21)
    at asyncGeneratorStep (E:\next_js\agenda\node_modules\@babel\runtime-corejs2\helpers\asyncToGenerator.js:5:24)
    at _next (E:\next_js\agenda\node_modules\@babel\runtime-corejs2\helpers\asyncToGenerator.js:27:9)

NOTE: if I remove withLayout() wrapping, the index.js pages work well again

IF usefull...

packages.json

{
  "scripts": {
    "dev": "cross-env DEBUG=* node server.js",
    "build": "next build",
    "start": "cross-env NODE_ENV=production node server.js"
  },
  "dependencies": {
    "@zeit/next-sass": "^1.0.1",
    "express": "^4.16.4",
    "next": "^7.0.2",
    "next-compose": "0.0.2",
    "node-sass": "^4.10.0",
    "react": "^16.6.3",
    "react-dom": "^16.6.3",
    "workbox-webpack-plugin": "^3.6.3"
  },
  "devDependencies": {}
}

To investigate, I added a console.log in the code of next/dist/server/render.js

        case 9:
          _ref3 = _context3.sent;
          _ref4 = (0, _slicedToArray2.default)(_ref3, 5);
          buildManifest = _ref4[0];
          reactLoadableManifest = _ref4[1];
          Component = _ref4[2];
          Document = _ref4[3];
          App = _ref4[4];
          Component = Component.default || Component;

          console.log(Component);     // <<------- I added this

          if (!(typeof Component !== 'function')) {
            _context3.next = 19;
            break;
          }

          throw new Error("The default export is not a React Component in page: \"".concat(page, "\""));

This is what I got

{ '$$typeof': Symbol(react.element),
  type: 'div',
  key: null,
  ref: null,
  props:
   { style: { margin: 20, padding: 20, border: '1px solid #DDD' },
     children: [ [Object], undefined ] },
  _owner: null,
  _store: {} }
Error: The default export is not a React Component in page: "/index"
    at _callee3$ (E:\next_js\agenda\node_modules\next\dist\server\render.js:244:21)
    at tryCatch (E:\next_js\agenda\node_modules\regenerator-runtime\runtime.js:62:40)
    at Generator.invoke [as _invoke] (E:\next_js\agenda\node_modules\regenerator-runtime\runtime.js:288:22)
    at Generator.prototype.(anonymous function) [as next] (E:\next_js\agenda\node_modules\regenerator-runtime\runtime.js:114:21)
    at asyncGeneratorStep (E:\next_js\agenda\node_modules\@babel\runtime-corejs2\helpers\asyncToGenerator.js:5:24)
    at _next (E:\next_js\agenda\node_modules\@babel\runtime-corejs2\helpers\asyncToGenerator.js:27:9)
    at process._tickCallback (internal/process/next_tick.js:68:7)

I hope someone could understand something, I'm not able to follow the reasoning that the renderer is doing in evaluating the component, sorry.

@realtebo in your case the issue is you are returning the React element, not a React component.

Change your lib/layout.js to

import Header from '../components/header'

const layoutStyle = {
  margin: 20,
  padding: 20,
  border: '1px solid #DDD'
}

const withLayout = Component => (props) => (
  <div style={layoutStyle}>
    <Header />
    <Component />
  </div>
)

export default withLayout

Or something like that. what's happening is your are using a normal component as a High Order Component, the correct syntax of a HOC is WrappedComponent => NewComponent where your NextComponent (in your case is withLayout) can use the WrappedComponent (your page) internally.

@sergiodxa : _mine_ is the suggested code here: https://nextjs.org/learn/basics/using-shared-components/the-layout-component

I don't know why _now_ it not works; it worked until yesterday. Sorry that this doesn't help.

I changed index.js and about.js to follow the suggested schema

// pages/index.js

import Layout from '../components/MyLayout.js'

export default () => (
    <Layout>
       <p>Hello Next.js</p>
    </Layout>
)

Now it works. So thanks @sergiodxa

PR here: #5095, it's a little outdated though

@realtebo if you check the page, you are using the layout as a HOC doing withLayout(Page) but the tutorial is using as a normal component.

import Layout from '../components/MyLayout.js'

export default () => (
    <Layout>
       <p>This is the about page</p>
    </Layout>
)

It just render Layout as a component wrapping the rest of the page. You can either use it that way or as a HOC, in the case of a HOC you need to follow the WrappedComponent => NewComponent syntax.

Thanks for the super detailed issue including codesandbox reproduction 馃挴馃檹 Let's track this issue in #4055

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kenji4569 picture kenji4569  路  3Comments

olifante picture olifante  路  3Comments

ghost picture ghost  路  3Comments

timneutkens picture timneutkens  路  3Comments

havefive picture havefive  路  3Comments