MUI и Preact в экспресс-приложении машинописного текста, получая сообщение об ошибке консоли «prop `children`, предоставленное `InnerThemeProvider`, ожидается ReactNode»

Я реализую приложение Preact и хотел бы использовать MUI. Кажется, что в VSCode все ссылается правильно, но когда я пытаюсь получить к нему доступ в браузере, ничего не отображается, и появляется эта ошибка консоли:

react-jsx-runtime.development.js:87 Warning: Failed prop type: Invalid prop `children` supplied to `InnerThemeProvider`, expected a ReactNode.
 at InnerThemeProvider (http://localhost:8080/main.js:54463:70)
 printWarning   @   react-jsx-runtime.development.js:87
 error  @   react-jsx-runtime.development.js:61
 checkPropTypes @   react-jsx-runtime.development.js:631
 validatePropTypes  @   react-jsx-runtime.development.js:1164
 jsxWithValidation  @   react-jsx-runtime.development.js:1284
 jsxWithValidationDynamic   @   react-jsx-runtime.development.js:1301
 ThemeProvider  @   ThemeProvider.js:34
 M  @   preact.js:1
 I  @   preact.js:1
 b  @   preact.js:1
 I  @   preact.js:1
 b  @   preact.js:1
 I  @   preact.js:1
 N  @   preact.js:1
 (anonymous)    @   main.tsx:24
 (anonymous)    @   main.tsx:24
 (anonymous)    @   main.tsx:24

Я пробовал возиться с webpack и tsconfig, но, честно говоря, если я не могу найти его в Интернете, я просто предполагаю, и не похоже, что у кого-то еще есть такая же проблема. Я думаю, что это должно быть псевдонимом, так как он упоминает реагирующие вещи.

Структура файла такова:

.
├── index.ts
├── main.pug
├── main.tsx
├── package.json
├── tsconfig.json
└── webpack.config.js

Интересует всего 6 маленьких файлов (я их урезал, насколько умею):

  1. index.ts: файл, который запускает сервер
import express = require("express");
import { Server } from 'http';
import { Application, Request, Response } from "express";

const PORT = 8080;

const setupServer: () => Promise<void> = async () => {
    let app = express();
    const http = new Server(app);
    
    app.use(express.json());
    app.use(express.static(__dirname));

    // setup pug template engine
    app.engine("pug", require("pug").__express);
    app.set("views",__dirname);

    app.get("*", async (req: Request, res: Response) => {
        res.render("./main.pug");
    });

    // console.info that your server is up and listening
    http.listen(PORT, function() {
        console.info(`Listening on port ${PORT}`);
    });
};
setupServer();
  1. main.tsx: файл поведения, на который ссылается файл pug.
import { h, render } from "preact";
import { Button, createTheme, ThemeProvider } from '@mui/material';
import { red } from '@mui/material/colors';

const theme = createTheme({
    palette: {
      primary: {
        main: red[500],
      },
    },
});

function Main() {
    return (
        <ThemeProvider theme = {theme}>
            <Button variant = "contained">Hello World</Button>
        </ThemeProvider>
    );
};
render(<Main />, document.body);
  1. webpack.config.js
"use strict";
const { merge } = require('webpack-merge');
const nodeExternals = require('webpack-node-externals');

module.exports = env => {

    // shared by all bundle configs
    const baseConfig = {
        mode: "development",
        devtool: "source-map",
        resolve: {
            extensions: [".js", ".json", ".ts", ".tsx"],
        },
        module: {
            rules: [
                {
                    test: /\.tsx?$/,
                    use: 'ts-loader',
                    exclude: /node_modules/,
                },
                {
                    test: /\.js$/,
                    exclude: /node_modules/,
                    use: ["babel-loader"]
                },
                {
                    test: /\.css$/,
                    use: [ 'style-loader', 'css-loader' ]
                }
            ]
        }
    };

    // client config
    const clientMainBundleConfig = merge(baseConfig,{
        name: 'main',
        entry: { 'main': __dirname + "/main.tsx" },
        target: 'web',
        output: {
            path:  __dirname,
            filename: "[name].js"
        },
        resolve: {
            alias: { 
                "preact/hooks": require.resolve('preact/hooks'),
                "preact": require.resolve('preact')
            }
        }
    });

    // the config for the index file on server side
    const serverBundleConfig = merge(baseConfig,{
        target: 'node',
        name: 'index',
        externals: [nodeExternals()],
        entry: { 'index':  __dirname + "/index.ts" },
        output: {
            path:  __dirname,
            filename: "[name].js"
        },
    });

    return [serverBundleConfig,clientMainBundleConfig];
};
  1. tsconfig.json
{
  "compilerOptions": {
    "jsx": "react",                           
    "jsxFactory": "h",                        
    "jsxFragmentFactory": "Fragment",         
    "baseUrl": "./",                   
    "target": "es6",                               
    "module": "commonjs",                                                     
    "strict": true,
    "moduleResolution": "node",
    "esModuleInterop": true,      
    "skipLibCheck": true,
    "sourceMap": true,
    "paths": {                              
      "react": ["./node_modules/preact/compat"],
      "react-dom": ["./node_modules/preact/compat"]
    },             
  },
  "exclude":[
    "./node_modules"
  ]
}
  1. пакет.json
{
  "name": "preact-mui",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --stats-error-details",
    "start": "node ./index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@emotion/core": "^11.0.0",
    "@emotion/react": "^11.10.5",
    "@emotion/styled": "^11.10.5",
    "@mui/material": "^5.11.0",
    "@types/express": "^4.17.15",
    "@types/express-session": "^1.17.5",
    "@types/node": "^18.11.15",
    "express": "^4.18.2",
    "http": "^0.0.1-security",
    "preact": "^10.11.3",
    "preact-cli": "^3.4.1",
    "preact-render-to-string": "^5.2.6",
    "preact-router": "^4.1.0",
    "pug": "^3.0.2",
    "ts-loader": "^9.4.2",
    "typescript": "^4.9.4",
    "webpack-cli": "^5.0.1",
    "webpack-merge": "^5.8.0",
    "webpack-node-externals": "^3.0.0"
  }
}
  1. main.pug (думаю, это не очень интересно, просто для полноты картины):
<!DOCTYPE html>
html(lang = "en")
  head
    meta(charset = "utf-8")
    meta(http-equiv = "X-UA-Compatible", content = "IE=edge")
    meta(name = "viewport", content = "width=device-width, initial-scale=1")
  body.container-fluid
    div#mainDiv
    script(type='text/javascript', src = "./main.js").

Я попытался совместить реакцию и реакцию-дом с preact/compat, надеясь, что, поскольку он упоминает реакцию, если я просто перенаправлю его обратно на preact, тогда он автоматически исправит себя, но низко и вот, это не сработало. Затем я попытался обновить tsconfig.json, включив в него следующее в соответствии с руководством mui:

  "compilerOptions": {
    "lib": ["es6", "dom"],
    "noImplicitAny": true,
    "noImplicitThis": true,
    "strictNullChecks": true,
    ...

Но все равно ничего не изменилось.

На самом деле ничего не отображается в google/SO, поэтому не знаю, куда идти дальше.


51
1

Ответ:

Решено

Похоже, вы неправильно поняли, что такое псевдоним.

Псевдоним дает X, когда запрашивается Y. В вашем коде вы присваиваете preact и preact/hooks псевдонимы самим себе, что вообще ничего не делает.

Теперь вам нужно использовать псевдоним, поскольку компоненты React и Preact имеют разную форму; вы не можете передать компонент React в Preact без возникновения ошибок (или наоборот). Однако preact/compat — это слой перевода, который облегчает это.

Вам нужно добавить это в свою базовую конфигурацию:

resolve: {
    alias: { 
        "react": "preact/compat",
        "react-dom/test-utils": "preact/test-utils",
        "react-dom": "preact/compat",     // Must be below test-utils
        "react/jsx-runtime": "preact/jsx-runtime"
    }
 }

https://preactjs.com/guide/v10/getting-started#aliasing-in-webpack