简体   繁体   中英

Material UI Select component crashing React application

I'm trying to add a material ui select component to the React application I'm working with. When I try to add a select functionality to a form, it crashes. Despite using the exact example from the docs , I get the same error and application crashes.

So far I've tried:

  • Tinkering with ids
  • Removing & changing various props to see if I can pin down a specific point that's causing the error (no luck)
  • Made sure value prop is an empty string, with a child matching MenuItem (no luck)
  • Set a defaultValue prop (no luck)
  • Removing other components to make sure the problem is centered on <Select> component (it is centered on Select . FormControl and InputLabel do not cause a crash)
  • Created a new route that only contains the example SimpleSelect component from from the docs (Still crashing, and this component is right below App component so nothing should be effecting it upstream)
  • Made sure @material-ui/core is up-to-date (4.11.0)
  • Removed node_modules and reinstalled everything
  • Scoured the web for other issues such as this, but haven't found anything

Error logs on the console are also generally cryptic:

The above error occurred in the <data:application/javascript;charset=utf-8;base64,aW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnOwppbXBvcnQgY3JlYXRlU3ZnSWNvbiBmcm9tICcuLi8uLi91dGlscy9jcmVhdGVTdmdJY29uJzsKLyoqCiAqIEBpZ25vcmUgLSBpbnRlcm5hbCBjb21wb25lbnQuCiAqLwoKZXhwb3J0IGRlZmF1bHQgY3JlYXRlU3ZnSWNvbiggLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoInBhdGgiLCB7CiAgZDogIk03IDEwbDUgNSA1LTV6Igp9KSwgJ0Fycm93RHJvcERvd24nKTs=> component:
    in data:application/javascript;charset=utf-8;base64,aW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnOwppbXBvcnQgY3JlYXRlU3ZnSWNvbiBmcm9tICcuLi8uLi91dGlscy9jcmVhdGVTdmdJY29uJzsKLyoqCiAqIEBpZ25vcmUgLSBpbnRlcm5hbCBjb21wb25lbnQuCiAqLwoKZXhwb3J0IGRlZmF1bHQgY3JlYXRlU3ZnSWNvbiggLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoInBhdGgiLCB7CiAgZDogIk03IDEwbDUgNSA1LTV6Igp9KSwgJ0Fycm93RHJvcERvd24nKTs= (created by ForwardRef(SelectInput))
    in ForwardRef(SelectInput) (created by ForwardRef(InputBase))
    in div (created by ForwardRef(InputBase))
    in ForwardRef(InputBase) (created by WithStyles(ForwardRef(InputBase)))
    in WithStyles(ForwardRef(InputBase)) (created by ForwardRef(FilledInput))
    in ForwardRef(FilledInput) (created by WithStyles(ForwardRef(FilledInput)))
    in WithStyles(ForwardRef(FilledInput)) (created by ForwardRef(Select))
    in ForwardRef(Select) (created by WithStyles(ForwardRef(Select)))
    in WithStyles(ForwardRef(Select)) (created by SimpleSelect)
    in div (created by ForwardRef(FormControl))
    in ForwardRef(FormControl) (created by WithStyles(ForwardRef(FormControl)))
    in WithStyles(ForwardRef(FormControl)) (created by SimpleSelect)
    in div (created by SimpleSelect)
    in SimpleSelect (created by Context.Consumer)
    in Route (created by App)
    in Switch (created by App)
    in ThemeProvider (created by App)
    in App
    in Router (created by BrowserRouter)
    in BrowserRouter

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/docs/error-boundaries.html to learn more about error boundaries.

and Uncaught DOMException: String contains an invalid character .

This is the component that generates this same error(exact copy from the docs):

import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import FormHelperText from '@material-ui/core/FormHelperText';
import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select';

const useStyles = makeStyles((theme) => ({
  formControl: {
    margin: theme.spacing(1),
    minWidth: 120,
  },
  selectEmpty: {
    marginTop: theme.spacing(2),
  },
}));

export default function SimpleSelect() {
  const classes = useStyles();
  const [age, setAge] = React.useState('');

  const handleChange = (event) => {
    setAge(event.target.value);
  };

  return (
    <div>
      <FormControl className={classes.formControl}>
        <InputLabel id="demo-simple-select-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-label"
          id="demo-simple-select"
          value={age}
          onChange={handleChange}
        >
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
      </FormControl>
      <FormControl className={classes.formControl}>
        <InputLabel id="demo-simple-select-helper-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-helper-label"
          id="demo-simple-select-helper"
          value={age}
          onChange={handleChange}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText>Some important helper text</FormHelperText>
      </FormControl>
      <FormControl className={classes.formControl}>
        <Select
          value={age}
          onChange={handleChange}
          displayEmpty
          className={classes.selectEmpty}
          inputProps={{ 'aria-label': 'Without label' }}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText>Without label</FormHelperText>
      </FormControl>
      <FormControl className={classes.formControl}>
        <InputLabel shrink id="demo-simple-select-placeholder-label-label">
          Age
        </InputLabel>
        <Select
          labelId="demo-simple-select-placeholder-label-label"
          id="demo-simple-select-placeholder-label"
          value={age}
          onChange={handleChange}
          displayEmpty
          className={classes.selectEmpty}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText>Label + placeholder</FormHelperText>
      </FormControl>
      <FormControl className={classes.formControl} disabled>
        <InputLabel id="demo-simple-select-disabled-label">Name</InputLabel>
        <Select
          labelId="demo-simple-select-disabled-label"
          id="demo-simple-select-disabled"
          value={age}
          onChange={handleChange}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText>Disabled</FormHelperText>
      </FormControl>
      <FormControl className={classes.formControl} error>
        <InputLabel id="demo-simple-select-error-label">Name</InputLabel>
        <Select
          labelId="demo-simple-select-error-label"
          id="demo-simple-select-error"
          value={age}
          onChange={handleChange}
          renderValue={(value) => `⚠️  - ${value}`}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText>Error</FormHelperText>
      </FormControl>
      <FormControl className={classes.formControl}>
        <InputLabel id="demo-simple-select-readonly-label">Name</InputLabel>
        <Select
          labelId="demo-simple-select-readonly-label"
          id="demo-simple-select-readonly"
          value={age}
          onChange={handleChange}
          inputProps={{ readOnly: true }}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText>Read only</FormHelperText>
      </FormControl>
      <FormControl className={classes.formControl}>
        <InputLabel id="demo-simple-select-autowidth-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-autowidth-label"
          id="demo-simple-select-autowidth"
          value={age}
          onChange={handleChange}
          autoWidth
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText>Auto width</FormHelperText>
      </FormControl>
      <FormControl className={classes.formControl}>
        <Select
          value={age}
          onChange={handleChange}
          displayEmpty
          className={classes.selectEmpty}
          inputProps={{ 'aria-label': 'Without label' }}
        >
          <MenuItem value="" disabled>
            Placeholder
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText>Placeholder</FormHelperText>
      </FormControl>
      <FormControl required className={classes.formControl}>
        <InputLabel id="demo-simple-select-required-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-required-label"
          id="demo-simple-select-required"
          value={age}
          onChange={handleChange}
          className={classes.selectEmpty}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText>Required</FormHelperText>
      </FormControl>
      <FormControl variant="outlined" className={classes.formControl}>
        <InputLabel id="demo-simple-select-outlined-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-outlined-label"
          id="demo-simple-select-outlined"
          value={age}
          onChange={handleChange}
          label="Age"
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
      </FormControl>
      <FormControl variant="filled" className={classes.formControl}>
        <InputLabel id="demo-simple-select-filled-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-filled-label"
          id="demo-simple-select-filled"
          value={age}
          onChange={handleChange}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
      </FormControl>
    </div>
  );
}

This might potentially be a build chain related issue, so here is my webpack config:

const webpack = require("webpack");
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");

const isProd = process.env.NODE_ENV === "production";
const buildDir = "dist";
const useBundleAnalyzer = false;
const chalk = require("chalk");
const version = JSON.stringify(require("./package.json").version);
const author = JSON.stringify(require("./package.json").author);

console.log(
  chalk.white.bgBlue.bold(`
-------------------------------------------------------------------------
            Application Front End
            Version: ${version}
            running: ${process.env.NODE_ENV}
            by: ${author}
-------------------------------------------------------------------------
`)
);

module.exports = {
  entry: {
    main: "./src/root.jsx",
  },
  mode: process.env.NODE_ENV !== "production" ? "development" : "production",
  optimization: {
    splitChunks: {
      chunks: "async",
      minSize: 30000,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: "~",
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
  output: {
    path: path.join(__dirname, buildDir),
    filename: "[name].[hash].js",
    chunkFilename: "[name].[id].[hash].chunk.js",
    publicPath: "/",
  },
  resolve: {
    modules: [path.join(__dirname, "./src"), "node_modules"],
    extensions: [".js", ".jsx"],
  },
  devServer: {
    hot: false,
    disableHostCheck: true,
    inline: true,
    contentBase: path.join("./", buildDir),
    historyApiFallback: true,
    headers: {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Credentials": "true",
      "Access-Control-Allow-Headers": "true",
    },
  },
  devtool: process.env.NODE_ENV === "development" ? "inline-source-map" : "",
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /(node_modules)/,
        loader: "babel-loader",
        query: {
          presets: ["@babel/preset-env", "@babel/preset-react"],
        },
      },
      {
        test: /\.css$|\.scss$|\.sass$/,
        loaders: [
          "style-loader",
          "css-loader",
          "resolve-url-loader",
          "sass-loader",
        ],
      },
      {
        test: /.(png|woff(2)?|eot|ttf|svg)/,
        loader: "url-loader?limit=100000",
      },
    ],
  },
  plugins: [
    ...(isProd ? [new CleanWebpackPlugin({ verbose: true })] : []),
    /**
     * Show visualization of webpack bundle.
     */
    ...(useBundleAnalyzer ? [new BundleAnalyzerPlugin()] : []),
    new webpack.DefinePlugin({
      "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
      VERSION: version,
    }),
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "./src/html/index.html"),
      filename: "index.html",
      inject: "body",
    }),
  ],
};

Has anyone ever encountered a similar problem? This problem stopped me dead in my tracks and is causing me a lot of frustration.

Edit: Icons are not the problem. Same thing is happening at below component as well:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import InputLabel from "@material-ui/core/InputLabel";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import Select from "@material-ui/core/Select";

const useStyles = makeStyles(theme => ({
  formControl: {
    margin: theme.spacing(1),
    minWidth: 120,
  },
  selectEmpty: {
    marginTop: theme.spacing(2),
  },
}));

export default function SimpleSelect2() {
  const classes = useStyles();

  return (
    <div>
      <FormControl className={classes.formControl}>
        <InputLabel id="demo-simple-select-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-label"
          id="demo-simple-select"
          value={10}
          onChange={event => console.log(event)}
        >
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
      </FormControl>
    </div>
  );
}

Edit-2:

This is the DOM after the crash:

崩溃后的 dom

Edit-3:

More descriptive error message at Chrome:

错误消息铬

I found the problem and fixed it. I'm writing a more comprehensive explanation as an answer so that anyone else who encounters a similar problem can benefit from it. Here's how I found it:

The 'cryptic' error message I posted in the question contains this string:

<data:application/javascript;charset=utf-8;base64,aW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnOwppbXBvcnQgY3JlYXRlU3ZnSWNvbiBmcm9tICcuLi8uLi91dGlscy9jcmVhdGVTdmdJY29uJzsKLyoqCiAqIEBpZ25vcmUgLSBpbnRlcm5hbCBjb21wb25lbnQuCiAqLwoKZXhwb3J0IGRlZmF1bHQgY3JlYXRlU3ZnSWNvbiggLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoInBhdGgiLCB7CiAgZDogIk03IDEwbDUgNSA1LTV6Igp9KSwgJ0Fycm93RHJvcERvd24nKTs=

The first part of the string told me this is a base64 encoded string. When I decoded it, this is what I got:

"import * as React from 'react';
import createSvgIcon from '../../utils/createSvgIcon';
/**
 * @ignore - internal component.
 */

export default createSvgIcon( /*#__PURE__*/React.createElement(\"path\", {
  d: \"M7 10l5 5 5-5z\"
}), 'ArrowDropDown');"

As the comment suggests, this is an internal component used by Material UI. It involves svg 's and based on that, I checked my webpack config to see how I'm handling svg 's and whether tinkering it would fix this problem.

Here's the piece of config where svg 's are handled in my application's webpack config:

      {
        test: /.(png|woff(2)?|eot|ttf|svg)/,
        loader: "url-loader?limit=100000",
      },

url-loader is causing the problem here and commenting it out fixes this issue. Removing the limit also changes nothing by the way. I'll use an another loader to go around this problem. So in the end this was a tooling issue.

Thanks @Jared Smith and @Ryan Cogswell for your comments. Both of you were close with your guesses and following them would eventually lead me to this discovery.

My solution:

Delete and clone the project again.

But I had a slightly different error:

The above error occurred in the <ForwardRef(SelectInput)> component:
at SelectInput (http://localhost:3001/static/js/bundle.js:23392:25)
at http://localhost:3001/static/js/bundle.js:6569:66
at div
at http://localhost:3001/static/js/bundle.js:6569:66

I managed to narrow the problem to this "value={act.name}"; if I completely remove the attribute, it renders, but of course, the select won't work.

{actions.map((act: Action) => {
    return (<MenuItem key={act.name} value={act.name} disabled={!hasAnyRoles(act.roles)}>{act.name}</MenuItem>)
})}

Oddly enough the problem appeared out of nothing, since one day it was working fine, and I even deployed on AWS server (maybe I missed something...). The next day, both my local and server versions were presenting this problem. On the server I just run the pipeline again (build and deploy) and it worked. On my computer, I tried all I could imagine. What solved the problem was deleting everything and cloning from git again.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM