简体   繁体   English

同构样式加载器为什么引发TypeError:与CSS-Modules一起使用时,无法读取未定义的属性“ apply”

[英]Why does isomorphic-style-loader throw a TypeError: Cannot read property 'apply' of undefined when being used in unison with CSS-Modules

I'm currently trying to render the application on the server, which works for the HTML and JS, but found that my styles (.less | .scss) would not load. 我目前正在尝试在服务器上呈现应用程序,该服务器适用于HTML和JS,但发现无法加载我的样式(.less | .scss)。 I did some research and figured, not sure, that I was missing the isomorphic-style-loader in my Webpack configuration based on others running into the same issues. 我进行了一些研究,并不确定(但不确定)基于其他遇到相同问题的Webpack配置中缺少isomorphic-style-loader I set it up, at least how I understood it, but am now finding that when running the application I receive the following error: 我对其进行了设置,至少是按照我的理解,但是现在发现在运行该应用程序时出现以下错误:

TypeError: Cannot read property 'apply' of undefined at WithStyles.componentWillMount

I'm somewhat new to the whole React / Express thing but have been trying to follow along with tutorials and learning as I go, if anything seems out of place, please excuse me. 我对整个React / Express有点陌生,但是一直在尝试跟随教程和学习,如果有什么地方不合适,请原谅。 I am hoping to see if anybody can explain what exactly causes this error, and provide me with some idea of what I could follow to resolve this error. 我希望看看是否有人可以解释导致此错误的确切原因,并为我提供一些解决该错误的方法的思路。 Below is some example code that resembles the one I am having issues with, if it helps in any way. 下面是一些示例代码,如果有任何帮助,它们类似于我遇到的代码。

(For reference I was following Tyler McGinnis React Router Server Rendering tutorial and tried to expand upon it to add styling - Link Here ) (作为参考,我正在关注Tyler McGinnis React Router Server Rendering教程,并尝试对其进行扩展以添加样式-Link Here

Thanks beforehand for any explanation provided as to what may be causing this error. 预先感谢您提供任何可能导致此错误的解释。


webpack.config.babel.js webpack.config.babel.js

import path from 'path'
import webpack from 'webpack'
import nodeExternals from 'webpack-node-externals'

const paths = {
  browser: path.join(__dirname, './src/browser'),
  server: path.join(__dirname, './src/server'),
  build: path.resolve(__dirname, 'public')
}

let browserConfig = {
  entry: `${paths.browser}/index.js`,
  output: {
    path: paths.build,
    filename: 'bundle.js',
    publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.s?(a|c)ss$/,
        use: [
          'isomorphic-style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 1,
              localIdentName: '[name]__[local]___[hash:base64:5]',
              sourceMap: true
            }
          },
          'sass-loader',
          'postcss-loader'
        ]
      }, {
        test: /\.less$/,
        use: [
          'isomorphic-style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 1,
              localIdentName: '[name]__[local]___[hash:base64:5]',
              sourceMap: true
            }
          },
          {
            loader: 'less-loader',
            options: {
              javascriptEnabled: true
            }
          },
          'postcss-loader'
        ]
      }, {
        test: /\.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      }
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      __isBrowser__: true
    })
  ],
  resolve: {
    extensions: ['.js', '.jsx', '.json', '.css', '.scss', '.sass', '.less']
  }
}

let serverConfig = {
  entry: `${paths.server}/index.js`,
  target: 'node',
  externals: [nodeExternals()],
  output: {
    path: __dirname,
    filename: 'server.js',
    publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.s?(a|c)ss$/,
        use: [
          'isomorphic-style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 1,
              localIdentName: '[name]__[local]___[hash:base64:5]',
              sourceMap: true
            }
          },
          'sass-loader',
          'postcss-loader'
        ]
      }, {
        test: /\.less$/,
        use: [
          'isomorphic-style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 1,
              localIdentName: '[name]__[local]___[hash:base64:5]',
              sourceMap: true
            }
          },
          {
            loader: 'less-loader',
            options: {
              javascriptEnabled: true
            }
          },
          'postcss-loader'
        ]
      }, {
        test: /\.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      }
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      __isBrowser__: false
    })
  ],
  resolve: {
    extensions: ['.js', '.jsx', '.json', '.css', '.scss', '.sass', '.less']
  }
}

module.exports = [browserConfig, serverConfig]

server.js server.js

import express from "express"
import cors from "cors"
import React from "react"
import bodyParser from 'body-parser'
import serialize from "serialize-javascript"
import { renderToString } from "react-dom/server"
import { StaticRouter, matchPath } from "react-router-dom"
import App from '../shared/App'
import routes from '../shared/routes'

const app = express()
const port = process.env.PORT || 3000

app.use(cors())
app.use(bodyParser.json()) // support json encoded bodies
app.use(bodyParser.urlencoded({extended: true})) // support encoded bodies
app.use(express.static("public"))

app.get("*", (req, res, next) => {
  const activeRoute = routes.find((route) => matchPath(req.url, route)) || {}

  const promise = activeRoute.fetchInitialData
    ? activeRoute.fetchInitialData(req.path)
    : Promise.resolve()

  promise.then((data) => {
    const css = new Set()
    const context = { data, insertCss: (...styles) => styles.forEach(style => css.add(style._getCss())) }
    const markup = renderToString(
      <StaticRouter location={req.url} context={context}>
          <App />
      </StaticRouter>
    )

    res.send(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>React on the Server!</title>
          <script src="/bundle.js" defer></script>
          <script>window.__INITIAL_DATA__ = ${serialize(data)}</script>
        </head>
        <body>
          <div id="app">${markup}</div>
        </body>
      </html>
    `)
  }).catch(next)
})

app.listen(port, () => console.log(`Server is listening on port: ${port}`))

routes.js routes.js

import AboutMain from './components/About/AboutMain'

const routes =  [
  {
    path: '/about',
    component: AboutMain
  }
]

export default routes

browser.js browser.js

// Import the neccessary modules for use in file
import React from 'react' // Main React module
import { hydrate } from 'react-dom' // render alternative for server rendering
import App from '../shared/App'
import { BrowserRouter } from 'react-router-dom' // React Router component for client side routing
import '../shared/components/global.scss' // Only has general rules, which do get applied

hydrate(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('app')
)

App.js App.js

import React, { Component } from 'react'
import routes from './routes'
import { Route, Link, Redirect, Switch } from 'react-router-dom'

class App extends Component {
  render() {
    return (
      <div>    
        <Switch>
          {routes.map(({ path, exact, component: Component, ...rest }) => (
            <Route key={path} path={path} exact={exact} render={(props) => (
              <Component {...props} {...rest} />
            )} />
          ))}
          <Route render={(props) => <NoMatch {...props} /> } />
        </Switch>
      </div>
    )
  }
}

export default App

AboutMain.js AboutMain.js

// Importing Required Modules
import React, {Component, Fragment} from 'react' // Importing React, Component, Fragment from "react"
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './about.scss'

class AboutMain extends Component {
  state = {
    phrase: 'We Made It!'
  }

  render() {
    return (
      <Fragment>
        <header className={s.banner}>
          <h1 className={s.heading}>{this.state.phrase}</h1>
        </header>
      </Fragment>
    )
  }
}

export default withStyles(s)(AboutMain) <-- Error Seems to occur here, at least I think.

about.scss about.scss

.banner {
  margin: 0 auto;
  padding: 15px;
  border: 2px solid #000;
}

.heading {
  text-transform: uppercase;
  text-decoration: underline;
}

The problem went away simply because you removed isomorphic-style-loader . 问题消失了,仅仅是因为您删除了isomorphic-style-loader Please don't accept your own answer like that. 请不要接受你自己的答案这样。 The problem here is you did not provide a context so insertCss.apply(_context, styles) will complain because _context is undefined . 这里的问题是您没有提供context所以insertCss.apply(_context, styles)将抱怨,因为_contextundefined To solve the problem, follow these steps: 要解决此问题,请按照下列步骤操作:

  1. Create a ContextProvider component in the same directory of App App的同一目录中创建ContextProvider组件

ContextProvider.js ContextProvider.js

import React from 'react';
import PropTypes from 'prop-types'
import App from './App'

class ContextProvider extends React.Component {
    static childContextTypes = {
      insertCss: PropTypes.func,
    }

    getChildContext() {
      return { ...this.props.context }
    }

    render () {
      return <App { ...this.props } />
    }
  }

  export default ContextProvider
  1. Wrap the ContextProvider in BOTH your browser.js and server.js . 包裹ContextProvider 两个browser.jsserver.js Remember to declare the context in both files. 记住声明context的两个文件。

browser.js (in other apps, this would be root client code, ie client.js or index.js ) browser.js(在其他应用中,这将是根的客户端代码,即client.jsindex.js

// Import the neccessary modules for use in file
/* import statements */


const context = {
    insertCss: (...styles) => {
      const removeCss = styles.map(x => x._insertCss());
      return () => {
        removeCss.forEach(f => f());
      };
    },
  }

hydrate(
  <BrowserRouter>
    //ContextProvider wraps around and returns App so we can do this
    <ContextProvider context={context} />
  </BrowserRouter>,
  document.getElementById('app')
)

server.js server.js

//Additional code above

app.get("*", (req, res, next) => {
  const activeRoute = routes.find((route) => matchPath(req.url, route)) || {}

  const promise = activeRoute.fetchInitialData
    ? activeRoute.fetchInitialData(req.path)
    : Promise.resolve()

  promise.then((data) => {
    const css = new Set()
    const context = { insertCss: (...styles) => 
styles.forEach(style => css.add(style._getCss()))}

    const markup = renderToString(
      <StaticRouter location={req.url}>
        <ContextProvider context={context}> 
         <App />
        </ContextProvider>
      </StaticRouter>
    )

    res.send(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>React on the Server!</title>
          <script src="/bundle.js" defer></script>
          <script>window.__INITIAL_DATA__ = ${serialize(data)}</script>
          <style type="text/css">${[...css].join('')}</style>
        </head>
        <body>
          <div id="app">${markup}</div>
        </body>
      </html>
    `)
  }).catch(next)
})

app.listen(port, () => console.log(`Server is listening on port: ${port}`))

I wrote an article explaining this in more detail here: https://medium.com/@danielnmai/load-css-in-react-server-side-rendering-with-isomorphic-style-loader-848c8140a096 我在这里写了一篇文章对此进行了更详细的解释: https : //medium.com/@danielnmai/load-css-in-react-server-side-rendering-with-isomorphic-style-loader-848c8140a096

After reviewing the code all night and endlessly searching Google. 整夜检查代码并无休止地搜索Google。 I found a fix that the main issue with my code was in the webpack.config.babel.js setup. 我找到了一个修复程序,该问题是我的代码的主要问题在于webpack.config.babel.js设置中。

I changed the browser test for sass || 我将浏览器测试更改为sass || scss to use style-loader , rather than the isomorphic-style-loader . scss使用style-loader ,而不是isomorphic-style-loader I also removed all isomorphic-style-loader logic from my app (ie. withStyles , insertCss , etc.) 我还从我的应用程序中删除了所有isomorphic-style-loader逻辑(即withStylesinsertCss等)

I'm not sure if this was the correct approach, but for the meantime, it seems to fix my problem and does not return any errors or network issues. 我不确定这是否是正确的方法,但与此同时,它似乎可以解决我的问题,并且不会返回任何错误或网络问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 同构式加载器,无法读取 null 的属性“应用” - Isomorphic-style-loader, Cannot read property 'apply' of null isomorphic-git clone() TypeError: 无法读取未定义的属性 'bind' - isomorphic-git clone() TypeError: Cannot read property 'bind' of undefined 为什么Angular 5 Transition抛出AppComponent.html:2 ERROR TypeError:无法读取未定义的属性&#39;forEach&#39; - Why does Angular 5 Transition throw AppComponent.html:2 ERROR TypeError: Cannot read property 'forEach' of undefined Webpack 4 css模块TypeError:无法读取未定义的属性“context” - Webpack 4 css modules TypeError: Cannot read property 'context' of undefined 为什么这些Jest测试失败了? TypeError:无法读取未定义的属性“apply” - Why are these Jest tests failing? TypeError: Cannot read property 'apply' of undefined 未捕获的TypeError:使用for循环时无法读取未定义的属性&#39;style&#39; - Uncaught TypeError: Cannot read property 'style' of undefined when using for loop “TypeError:无法读取属性'apply'of undefined” - “TypeError: Cannot read property 'apply' of undefined” 类型错误:无法在 EventEmitter 读取未定义的属性“应用” - TypeError: Cannot read property 'apply' of undefined at EventEmitter 类型错误:无法读取未定义 NodeJS 的属性“样式” - TypeError: Cannot read property 'style' of undefined NodeJS 未捕获的 TypeError 无法读取未定义的属性“样式” - Uncaught TypeError Cannot read property 'style' of undefined
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM