I'm using Fluxible to help create an isomorphic app on a new project and it's going swimmingly. I love it so far. I have run into a speed bump, though, and wonder how to get over it.
Here is my Header component thus far:
import React from 'react'
import Nav from '../Nav/Nav'
import classNames from 'classnames'
if (process.env.BROWSER) var styles = require('./Header.css')
class Header extends React.Component {
render() {
// Header classes
var theClasses = process.env.BROWSER ? classNames({
[styles.Header]: true
}) : ''
return (
<header className={theClasses}>
<Nav selected={this.props.selected} links={this.props.links} />
</header>
)
}
}
export default Header
You'll see I'm using process.env.BROWSER to detect which ENV I'm on. If we're in the client, I require the CSS. If we're on the server, I skip it. That works wonderfully.
The problem comes later in the file, where I build theClasses object based on the contents of the Header.css file, then use those classes on the Header like so:
<header className={theClasses}>
<Nav selected={this.props.selected} links={this.props.links} />
</header>
The problem is because I'm not loading the css on the server, theClasses ends up being empty, and the content rendered for the client ends up different than the content on the server. React displays this warning:
Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
(client) n28"><header class="Header--Header_ip_OK
(server) n28"><header class="" data-reactid=".2ei
What would you recommend to get this resolved?
The original issue was that I couldn't get CSS to compile on the server side, so I started checking for the BROWSER like this:
if (process.env.BROWSER) var styles = require('./Application.css')
If I remove the if (process.env.BROWSER) bit I get this error:
SyntaxError: src/components/Application/Application.css: Unexpected token (2:0)
1 |
> 2 | @import 'styles/index.css';
| ^
3 |
In the following simple CSS file:
@import 'styles/index.css';
.Application {
box-shadow: 0 0 0 1px var(--medium-gray);
box-sizing: border-box;
lost-center: 1080px 32px;
}
I started this project with the Fluxible Yo Generator which provides two Webpack config files here: https://github.com/yahoo/generator-fluxible/tree/master/app/templates
I updated mine with a few loaders:
var webpack = require('webpack');
var path = require('path');
module.exports = {
resolve: {
extensions: ['', '.js', '.jsx']
},
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./client.js'
],
output: {
path: path.resolve('./build/js'),
publicPath: '/public/js/',
filename: 'main.js'
},
module: {
loaders: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loaders: [
require.resolve('react-hot-loader'),
require.resolve('babel-loader')
]
}, {
test: /\.css$/,
loader: 'style-loader!css-loader?modules&localIdentName=[name]__[local]_[hash:base64:5]!postcss-loader'
}, {
test: /\.(png|jpg|svg)$/,
loader: 'url?limit=25000'
}, {
test: /\.json$/,
loader: 'json-loader'
}
]
},
postcss: function () {
return [
require('lost'),
require('postcss-import')({
path: ['./src/'],
onImport: function (files) {
files.forEach(this.addDependency);
}.bind(this)
}),
require('postcss-mixins'),
require('postcss-custom-properties'),
require('autoprefixer')({
browsers: ['last 3 versions']
})
];
},
node: {
setImmediate: false
},
plugins: [
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
BROWSER: JSON.stringify(true)
}
})
],
devtool: 'eval'
};
So that's where I am… not sure how to get CSS compiled server side. Appreciate any help I can get.
You can try css-modules-require-hook
webpack.config.js
{
test: /\.css$/,
loader: 'style-loader!css-loader?modules&localIdentName=[name]__[local]___[hash:base64:5]'
}
server.js
require('css-modules-require-hook')({
// This path should match the localIdentName in your webpack css-loader config.
generateScopedName: '[name]__[local]___[hash:base64:5]'
})
You need to ensure the server is generating the same markup as the client, which means that you need to use CSS Modules on the server too.
We achieve this in my current project by using Webpack to compile our Node code too. James Long wrote a really good guide on how to set this up, spread across three blog posts:
Backend Apps with Webpack (Part I)
Backend Apps with Webpack (Part II)
Backend Apps with Webpack (Part III)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With