样式化从索引文件导入的React组件时出错,但直接从组件文件导入时不出错

时间:2019-04-06 18:42:00

标签: javascript reactjs webpack styled-components

在这里是新手,所以如果我忽略了明显的内容或错过了重复的发帖(也因为没有直接在样式组件上发帖,显然我需要代表...),请提前道歉。

我发现与问题最接近的是该已解决的问题,位于线程底部:  https://github.com/styled-components/styled-components/issues/213

这个人有一个非常相似的问题,但是解决方案很简短,而且特定于Typescript。

背景:我有一个索引文件,该文件从各处导入我的组件,并以webpack别名导出它们。每当我需要导入其他地方时,我只需声明:

import { MyComponentA, MyComponentB, MyComponentC } from 'components';

我最近开始将样式化组件合并到我的项目中,当我尝试对以这种方式导入的组件进行样式设置时,出现了如下错误:

Error: Cannot create styled-component for component: undefined._____________styled-components.browser.esm.js:233
   at new StyledComponentsError (/home/jhillert/Dropbox/Projects/TimeLockr/node_modules/styled-components/dist/styled-components.browser.esm.js:233:59)
   at constructWithOptions (/home/jhillert/Dropbox/Projects/TimeLockr/node_modules/styled-components/dist/styled-components.browser.esm.js:1355:12)
   at styled (/home/jhillert/Dropbox/Projects/TimeLockr/node_modules/styled-components/dist/styled-components.browser.esm.js:2293:11)
   at Module.eval (/home/jhillert/Dropbox/Projects/TimeLockr/src/components/card-area/CardArea.jsx:111:91)
   at eval (/home/jhillert/Dropbox/Projects/TimeLockr/src/components/card-area/CardArea.jsx:133:31)
   at Module../src/components/card-area/CardArea.jsx (http://localhost:8080/bundle.js:9343:1)
   at __webpack_require__ (http://localhost:8080/bundle.js:724:30)
   at fn (http://localhost:8080/bundle.js:101:20)
   at eval (/home/jhillert/Dropbox/Projects/TimeLockr/src/indexes/components.jsx:17:89)
   at Module../src/indexes/components.jsx (http://localhost:8080/bundle.js:9619:1)

如果我直接从源文件导入组件,则不会收到错误消息。仅当我尝试将从索引文件导入的组件传递到styled()方法中时,以及当我尝试使用CSS属性添加内联样式时,这种情况才会发生。

由于某种原因,以这种方式导入的所有组件都不会发生该错误,并且我对代码进行了更改,这些更改突然导致索引导入触发该错误。我真的很喜欢有一个中央索引文件,因此将不胜感激。

css prop导致错误的示例:

*下面的项目配置。 索引文件如下所示(components.jsx):

export { default as ActionBar } from '../components/action-bar/ActionBar';
export { default as AuthForm } from '../components/auth/AuthForm';
export { default as AuthModal } from '../components/auth/AuthModal';
export { default as AuthTabs } from '../components/auth/AuthTabs';
export { default as Box } from '../components/box/Box';

/*==============================================================*/

//CardArea.js
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import {
  CardColumn  ◀◀◀—————————————— DOES PRODUCE ERROR
  LockedEntryCard,
  ReleasedEntryCard,
} from 'components';

import CardColumn from '../card-column/CardColumn' ◀◀◀——————— DOES NOT

const S = {};

S.CardArea = styled.div`  ◀◀◀————— NOT IMPORTED, NOT A PROBLEM
     /* ommitted */                BUT WOULD CAUSE SAME ERROR 
`;                                 IF IMPORTED.

const CardArea = ({ entries, refresh }) => {
  const { locked, released } = entries;
  const hasLockedChildren = !!(locked.length);
  const hasReleasedChildren = !!(released.length);

  const [showLocked, updateShowLocked] = useState(false);
  const [showReleased, updateShowReleased] = useState(false);

  useEffect(() => {
      updateShowLocked(true);
      updateShowReleased(true);
    },
    []);

  return (
    <S.CardArea>
      {(hasReleasedChildren && showReleased)
        && (
          <CardColumn
            id='card-column-released-entries'
            css='                   ◀◀◀ TRIGGERS THE ERROR
              margin-left = 2rem;   ◀◀◀ TRIGGERS THE ERROR
              margin-right = 1rem;  ◀◀◀ TRIGGERS THE ERROR
            '                       ◀◀◀ TRIGGERS THE ERROR
            heading='Unlocked'
            Card={ReleasedEntryCard}

      /* ommitted */

CardArea.propTypes = {
    /* ommitted */
};

export default CardArea;

================================================ ==================

Webpack配置(webpack.config.js):

别名朝底部。

const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const Dotenv = require('dotenv-webpack');
const path = require('path');

const config = {
  entry: './src/index.jsx',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: 'babel-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.jsx?$/,
        include: /node_modules/,
        use: ['react-hot-loader/webpack'],
      },
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader/locals',
        ],
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'less-loader',
        ],
      },
      {
        test: /\.png$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              mimetype: 'image/png',
            },
          },
        ],
      },
    ],
  },
  node: {
    console: true,
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
  },
  plugins: [
    new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/),
  ],
  plugins: [
    new CopyWebpackPlugin([
      // relative path is from src
      { from: './static/favicon.ico' }, // <- your path to favicon
    ]),
    new Dotenv({
      systemvars: true,
    }),
  ],
  devServer: {
    contentBase: './dist',
  },
  resolve: {
    extensions: ['.js', '.jsx', '.css'],
    alias: {
      components: path.resolve(__dirname, 'src/indexes/components.jsx'),
      contexts: path.resolve(__dirname, 'src/indexes/contexts.jsx'),
      theme: path.resolve(__dirname, 'src/indexes/theme.jsx'),
      utilities: path.resolve(__dirname, 'src/indexes/utilities.jsx'),
    },
  },
};

module.exports = config;

Babel Config(.bablerc):

{
  presets: [
    [
      '@babel/preset-env',
      {
        modules: false
      }
    ],
    '@babel/preset-react'
  ],
  plugins: [
    'react-hot-loader/babel',
    '@babel/plugin-proposal-class-properties',
    '@babel/plugin-transform-react-jsx-source',
    [
      'babel-plugin-styled-components',
      {
        "ssr": false
      }
    ],
  ]
}

依赖关系(Package.json)

{
  "name": "timelockr",
  "version": "1.0.0",
  "description": "Making information inaccessible.",
  "main": "index.js",
  "keywords": [],
  "author": "",
  "license": "ISC",
  "repository": {
    "type": "git",
    "url": "https://github.com/jehillert/Keep-Away"
  },
  "scripts": {
    "clean": "rm dist/bundle.js",
    "build-dev": "webpack -d --mode development",
    "build-prod": "webpack -p --mode production",
    "dev-server": "nodemon --ignore node_modules --inspect server",
    "start": "webpack-dev-server --host 0.0.0.0 --hot --mode development",
    "start:dev": "nodenv -f .",
    "lint:css": "stylelint './src/**/*.js'"
  },
  "dependencies": {
    "@date-io/date-fns": "^1.1.0",
    "@date-io/moment": "^1.1.0",
    "@material-ui/core": "^3.9.3",
    "@material-ui/icons": "^3.0.2",
    "@material-ui/styles": "^4.0.0-alpha.4",
    "@material-ui/system": "^3.0.0-alpha.2",
    "ajv": "^6.10.0",
    "axios": "^0.18.0",
    "bluebird": "^3.5.3",
    "body-parser": "^1.18.3",
    "bootstrap": "^4.3.1",
    "classnames": "^2.2.6",
    "cors": "^2.8.5",
    "date-fns": "^2.0.0-alpha.27",
    "debug": "^4.1.1",
    "dotenv-webpack": "^1.7.0",
    "express": "^4.16.4",
    "express-mysql-session": "^2.1.0",
    "express-session": "^1.15.6",
    "jquery": "^3.3.1",
    "less": "^3.9.0",
    "material-ui-pickers": "^2.2.4",
    "moment": "^2.24.0",
    "mysql": "^2.16.0",
    "notistack": "^0.6.1",
    "pbkdf2-password": "^1.2.1",
    "prop-types": "^15.7.2",
    "react": "^16.8.6",
    "react-bootstrap": "^1.0.0-beta.6",
    "react-copy-to-clipboard": "^5.0.1",
    "react-dom": "^16.8.6",
    "react-hot-loader": "^4.8.2",
    "react-router-dom": "^5.0.0",
    "react-transition-group": "^2.7.1",
    "styled-components": "^4.2.0",
    "typeface-roboto": "0.0.54"
  },
  "devDependencies": {
    "@babel/core": "^7.4.0",
    "@babel/plugin-proposal-class-properties": "^7.4.0",
    "@babel/preset-env": "^7.4.2",
    "@babel/preset-react": "^7.0.0",
    "async": "^2.6.2",
    "babel-eslint": "^10.0.1",
    "babel-loader": "^8.0.5",
    "babel-plugin-styled-components": "^1.10.0",
    "babel-plugin-transform-react-jsx-source": "^6.22.0",
    "copy-webpack-plugin": "^5.0.2",
    "css-loader": "^1.0.0",
    "eslint": "^5.16.0",
    "eslint-config-airbnb": "^17.1.0",
    "eslint-import-resolver-webpack": "^0.11.0",
    "eslint-plugin-import": "^2.16.0",
    "eslint-plugin-jsx-a11y": "^6.2.1",
    "eslint-plugin-react": "^7.12.4",
    "eslint-plugin-react-hooks": "^1.6.0",
    "eslint_d": "^7.3.0",
    "faker": "^4.1.0",
    "less-loader": "^4.1.0",
    "node-cmd": "^3.0.0",
    "nodemon": "^1.18.10",
    "style-loader": "^0.23.1",
    "stylelint": "^9.10.1",
    "stylelint-config-recommended": "^2.1.0",
    "stylelint-config-styled-components": "^0.1.1",
    "stylelint-processor-styled-components": "^1.6.0",
    "url-loader": "^1.1.2",
    "webpack": "^4.29.6",
    "webpack-cli": "^3.3.0",
    "webpack-dev-server": "^3.2.1"
  }
}

1 个答案:

答案 0 :(得分:0)

arc回购中的以下github问题似乎描述了与您在此处遇到的问题类似的问题:#339#130。上一个问题特别包含this comment,它建议一个可能的解决方案。在大多数情况下,通过以下方式进行样式处理可以解决该问题:

const StyledCComponent = styled(props => <CComponent {...props} />)``

根本原因似乎是循环依赖问题。通过将组件包装在函数中,可以在运行时评估依赖关系。