The current implementation of _app.js, _document.js requires you to extend the App and the Document class exported by Next. Since hooks cannot be added in class based components, it is currently not possible to use hooks here
Export a functional implementation of App, Document, Error. Or maybe we can provide hooks that address the functionality that these offer.
Currently, we could add a top-level component to _app.js and pass the page as a child to this component. This component would be functional, and can use hooks to define state, and add effects.
Can this be looked at again?
I'm trying to create a custom _app.js file in an npm package so that I can simply use the following as project/pages/_app.js:
import AppProvider from "myPackage/customApp.js";
export default AppProvider;
However this fails as the Next.js only works if the npm package is in ES5, which doesn't support classes.
For example, compiling this:
import React from "react";
import App, { Container } from "next/app";
import CssBaseline from "@material-ui/core/CssBaseline";
import { ThemeProvider } from "@material-ui/styles";
import theme from "../theme/createTheme";
import { StateProvider } from "../state/store";
import Snackbar from "../component/Snackbar";
// ::::::::::::::::::::::::::::::::::::::::::::::::
// Component
// ::::::::::::::::::::::::::::::::::::::::::::::::
class AppProvider extends App {
componentDidMount(): void {
// Remove the server-side injected CSS
const jssStyles = document.querySelector("#jss-server-side");
if (jssStyles && jssStyles.parentNode) {
jssStyles.parentNode.removeChild(jssStyles);
}
}
render(): JSX.Element {
const { Component, pageProps } = this.props;
return (
<Container>
<StateProvider>
<ThemeProvider theme={theme}>
<CssBaseline />
<Component {...pageProps} />
<Snackbar />
</ThemeProvider>
</StateProvider>
</Container>
);
}
}
export default AppProvider;
Turns it into:
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = __importDefault(require("react"));
var app_1 = __importStar(require("next/app"));
var CssBaseline_1 = __importDefault(require("@material-ui/core/CssBaseline"));
var styles_1 = require("@material-ui/styles");
var createTheme_1 = __importDefault(require("../theme/createTheme"));
var store_1 = require("../state/store");
var Snackbar_1 = __importDefault(require("../component/Snackbar"));
var AppProvider = (function (_super) {
__extends(AppProvider, _super);
function AppProvider() {
return _super !== null && _super.apply(this, arguments) || this;
}
AppProvider.prototype.componentDidMount = function () {
var jssStyles = document.querySelector("#jss-server-side");
if (jssStyles && jssStyles.parentNode) {
jssStyles.parentNode.removeChild(jssStyles);
}
};
AppProvider.prototype.render = function () {
var _a = this.props, Component = _a.Component, pageProps = _a.pageProps;
return (react_1.default.createElement(app_1.Container, null,
react_1.default.createElement(store_1.StateProvider, null,
react_1.default.createElement(styles_1.ThemeProvider, { theme: createTheme_1.default },
react_1.default.createElement(CssBaseline_1.default, null),
react_1.default.createElement(Component, __assign({}, pageProps)),
react_1.default.createElement(Snackbar_1.default, null)))));
};
return AppProvider;
}(app_1.default));
exports.default = AppProvider;
Which then causes the following error when I try to run npm run dev or npm run build:
TypeError: Class constructor App cannot be invoked without 'new'
at new AppProvider (C:\...\node_modules\myPackage\customApp.js:47:42)
at processChild (C:\...\node_modules\react-dom\cjs\react-dom-server.node.development.js:2846:14)
at resolve (C:\...\node_modules\react-dom\cjs\react-dom-server.node.development.js:2812:5)
at ReactDOMServerRenderer.render (C:\...\node_modules\react-dom\cjs\react-dom-server.node.development.js:3202:22)
at ReactDOMServerRenderer.read (C:\...\node_modules\react-dom\cjs\react-dom-server.node.development.js:3161:29)
at renderToString (C:\...\node_modules\react-dom\cjs\react-dom-server.node.development.js:3646:27)
at render (C:\...\node_modules\next-server\dist\server\render.js:85:16)
at renderPage (C:\...\node_modules\next-server\dist\server\render.js:234:20)
at ctx.renderPage (C:\...\node_modules\myPackage\customDocument.js:100:28)
at C:\...\node_modules\next\dist\pages\_document.js:4:232
at Generator.next (<anonymous>)
at asyncGeneratorStep (C:\...\node_modules\@babel\runtime-corejs2\helpers\asyncToGenerator.js:5:24)
at _next (C:\...\node_modules\@babel\runtime-corejs2\helpers\asyncToGenerator.js:27:9)
at C:\...\node_modules\@babel\runtime-corejs2\helpers\asyncToGenerator.js:34:7
at new Promise (<anonymous>)
at new F (C:\...\node_modules\core-js\library\modules\_export.js:36:28)
@TidyIQ same issue, after upgrade to next.9