I am using Webpack 3.7.1 and React 15.6.1 and I am trying to load different components dynamically.
What I would like to do
Loading the components asynchronously from the different chunks webpack created when code splitting
What i did
Using getComponent() and import() to generate the chunks
Configured the webpack.config file properly so that the chunks are created (code splitting)
The issue
Chunks are generated but not loaded properly when accessing a route
getComponent() does not seem to work
My Webpack.config file
module.exports = {
devServer: {
historyApiFallback: true
},
entry: {
app:"./src/index.js",
vendor: [
"axios",
"react",
"react-dom",
"react-redux",
"react-router",
"react-router-dom",
"redux"
]
},
output: {
path: __dirname + '/public/views',
filename: '[name].js',
chunkFilename: '[chunkhash].chunk.js',
publicPath: "/views/"
},
module: {
loaders: [
{
test: /\.js$/,
loader: "babel-loader",
exclude: [/node_modules/, /pdfmake.js$/]
},
{
test: /\.json$/,
loader: "json-loader"
}
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
minChunks: Infinity
}),
new webpack.NamedModulesPlugin(),
new HtmlWebpackPlugin({
filename: __dirname + "/views/index.ejs",
template: __dirname + "/views/template.ejs",
inject: 'body',
chunks: ['vendor', 'app'],
chunksSortMode: 'manual'
}),
new PreloadWebpackPlugin({
rel: "preload",
include: ["vendor", "app"]
}),
new webpack.optimize.OccurrenceOrderPlugin(),
]
};
My index.js file (root of my react app)
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import promise from "redux-promise";
import reducers from "./reducers";
import AppInit from "./containers/appInit";
import ProfRegisteringModal from "./containers/modals/register_prof_explanation_modal";
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);
function errorLoading(err) {
console.error("Dynamic page loading failed", err);
}
function loadRoute(cb) {
return module => cb(null, module.default);
}
console.log("testst");
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<AppInit>
<BrowserRouter>
<div style={{ height: "100%" }}>
<ProfRegisteringModal />
<Switch>
<Route
path="/inscription/:user"
getComponent={(location, callback) => {
import(
"./components/registering/registering_landing_page.js"
)
.then(loadRoute(cb))
.catch(errorLoading);
}}
/>
<Route
path="/inscription"
getComponent={(location, callback) => {
import(
"./components/registering/registering_landing_page.js"
)
.then(loadRoute(cb))
.catch(errorLoading);
}}
/>
<Route
path="/connexion"
getComponent={(location, callback) => {
import("./containers/registering/signing_in.js")
.then(loadRoute(cb))
.catch(errorLoading);
}}
/>
<Route
path="/equipe"
getComponent={(location, callback) => {
import("./components/team_pres.js")
.then(loadRoute(cb))
.catch(errorLoading);
}}
/>
<Route
path="/"
getComponent={(location, callback) => {
import("./containers/app_container.js")
.then(loadRoute(cb))
.catch(errorLoading);
}}
/>
</Switch>
</div>
</BrowserRouter>
</AppInit>
</Provider>,
document.querySelector(".root")
);
This file got correctly loaded as I can see the console.log("test") appearing in my console.
None of the components are correctly loaded when accessing any of the routes.
Thank you very much for your help
I think what your code is missing is a way to trigger an update.
I remember solving this issue by creating a wrapper around the import() promise.
// AsyncComponent.js
export default function wrapper(importComponent) {
class AsyncComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
Comp: null
};
}
componentDidMount() {
importComponent()
.then(Comp => this.setState({
Comp
}))
.catch(err => this.setState({
error: err
}));
}
render() {
if(this.state.error) {
return <h2> Loading error
<button onClick={e => this.componentDidMount()}> Try again </button>
</h2>
}
const Comp = this.state.Comp;
return Comp ?
<Comp {...this.props} /> :
<div> Still Loading: You can add a spinner here </div>
}
}
return AsyncComponent;
}
// Routes.js
import AsyncComponent from './component/AsyncComponent';
const Users = AsyncComponent(() => import(/* webpackChunkName:"users" */ './Users'))
const Home = AsyncComponent(() => import(/* webpackChunkName:"home" */ './Home'))
const Equipe = AsyncComponent(() => import(/* webpackChunkName:"equipe" */ './Equipe'))
<Route path='/users' component={Users} />
<Route path='/equipe' component={Equipe} />
Related
I have this app which I'm rendering on the server side. Everything was working fine until I tried to add Material UI to it.
My directory structure is this:
app/
build/ * This is created by webpack
server_bundle.js
public/
client_bundle.js
fonts/
images/
src/
client/
Client.tsx
server/
server.tsx
shared/
App.tsx
routes.tsx
webpack.config.js
And here are my files content:
webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
target: 'node',
entry: {
server: path.resolve(__dirname, 'src/server/server.tsx'),
"public/client": path.resolve(__dirname, 'src/client/client.tsx')
},
output: {
filename: '[name]_bundle.js',
path: path.resolve(__dirname, 'build'),
publicPath: '/build'
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
},
module: {
rules: [{
test: /\.(tsx|ts)?$/,
loader: 'awesome-typescript-loader',
options: {
jsx: 'react'
}
},
{
test: /\.(scss|sass|css)$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
]
},
{
test: /\.(ico)$/,
loader: 'file-loader',
options: { outputPath: '/public', publicPath: '/public', name: '[name].[ext]' }
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
loader: 'file-loader',
options: { outputPath: '/public/images', publicPath: 'images' }
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
loader: 'file-loader',
options: { outputPath: '/public/fonts', publicPath: 'fonts' }
},
]
},
optimization: {
},
plugins: [
new MiniCssExtractPlugin({
filename: 'public/styles_bundle.css',
chunkFilename: "public/styles/[id].css"
})
]
}
server.tsx
import * as express from "express";
import * as bodyParser from "body-parser";
import * as React from "react";
import * as ReactDOMServer from "react-dom/server";
import {StaticRouter} from "react-router";
import { matchPath } from "react-router-dom";
import {Helmet} from "react-helmet";
import App from "../shared/App";
import routes from '../shared/routes';
const app = express();
const PORT = process.env.PORT || 3000;
app.use(bodyParser.urlencoded());
app.use(bodyParser.json());
app.use(express.static("build/public"));
app.get('*', (req, res, next) => {
const activeRoute = routes.find(route => !!matchPath(req.url, route)) || {path: "/"};
const now = new Date();
console.log(`GET ${now} - ${req.url}`);
const context = {}
const content = ReactDOMServer.renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
const helmet = Helmet.renderStatic();
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
${helmet.title.toString()}
${helmet.meta.toString()}
<link rel="stylesheet" href="styles_bundle.css">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
</head>
<body>
<div id="root" style="overflow-x: hidden; width: 100%; margin: 0;">${content}</div>
<script src="client_bundle.js"></script>
</body>
</html>
`;
res.send(html);
});
app.listen(PORT, () => {
console.log(`App is running on port ${PORT}`)
})
Client.tsx
import * as React from "react";
import * as ReactDOM from 'react-dom';
import { BrowserRouter } from "react-router-dom";
import App from "../shared/App";
// Because it's already been rendered, we only need to hydrate event
// handlers and wire things up.
ReactDOM.hydrate(
<BrowserRouter>
<App />
</BrowserRouter>,
document.querySelector("#root")
);
App.tsx
import * as React from 'react';
import routes from "../shared/routes";
import { Helmet } from 'react-helmet';
import { Switch, Route } from "react-router";
import 'typeface-roboto';
class App extends React.Component {
render() {
return (
<React.Fragment>
<Switch>
{routes.map(({path, exact, component: C}) => {
return <Route
path={path}
exact={exact}
render={(props) => <C {...props}/> }
/>
})}
</Switch>
</React.Fragment>
)
}
}
export default App;
And finally, routes.tsx
import * as React from 'react';
import { Button } from '#material-ui/core';
const routes = [
{
name: "Home",
exact: true,
path: "/",
component: (props:any) => {return (
<Button variant="contained" color="primary">
Hello World
</Button>
)}
}
];
export default routes;
I get this error in my browser console and obviously no material ui styles whatsoever:
client_bundle.js:44 Uncaught ReferenceError: global is not defined
at Object.<anonymous> (client_bundle.js:44)
at n (client_bundle.js:1)
at Object.<anonymous> (client_bundle.js:1)
at n (client_bundle.js:1)
at Object.<anonymous> (client_bundle.js:17)
at n (client_bundle.js:1)
at Object.<anonymous> (client_bundle.js:17)
at n (client_bundle.js:1)
at Object.<anonymous> (client_bundle.js:36)
at n (client_bundle.js:1)
That part of the client_bundle.js looks like this:
... __esModule",{value:!0});global.CSS;t.default=function(e){return e}} ...
What do you think might be happenning here??
try this workaround in your
plugins: [
new webpack.DefinePlugin({
'global': {} // webpack workaround
}),
new MiniCssExtractPlugin({
filename: 'public/styles_bundle.css',
chunkFilename: "public/styles/[id].css"
})
]
I have components/Header/index.jsx looking like this:
import React from 'react';
import PropTypes from 'prop-types';
import Breadcrumb from '../Breadcrumb';
// import styled from 'styled-components';
import Logo from '../Logo';
/* eslint-disable react/prefer-stateless-function */
class Header extends React.Component {
render() {
const providerId = (this.props.profileData.profileData.length > 0) ? this.props.profileData.profileData[0].provider_id : null;
if (!providerId) {
return "Loading...";
}
const certifStatus = (this.props.profileData.profileData.length > 0) ? this.props.profileData.profileData[0].certification_status : null;
let showInfo = false;
if (certifStatus === 'certified'){
showInfo = true;
}
return (
<div className="header">
<div className="header__top">
<div className="container-fluid">
<div className="row">
<div className="col-12">
<a href="/" className="header__logo">
<Logo providerId={providerId} />
</a>
<span style={{ marginLeft: '4px' }} className="header__title">
{this.props.text}
</span>
</div>
</div>
</div>
</div>
<Breadcrumb text="Artist certification" link="https://www.believebackstage.com/" showInfo={showInfo} infoLink="#"/>
</div>
);
}
}
Header.propTypes = {
profileData: PropTypes.object,
text: PropTypes.string,
};
export default Header;
When I try to import it into containers/ProfilePage/index.js
import Header from '../../components/Header/index.jsx';
It throws:
ERROR in ./app/components/Header/index.jsx 28:6
Module parse failed: Unexpected token (28:6)
You may need an appropriate loader to handle this file type.
| }
| return (
> <div className="header">
| <div className="header__top">
| <div className="container-fluid">
# ./app/containers/ProfilePage/index.js 30:0-55 69:28-34
# ./app/containers/ProfilePage/Loadable.js
# ./app/containers/App/index.js
# ./app/app.js
# multi eventsource-polyfill webpack-hot-middleware/client?reload=true ./app/app.js
It seems as if it is a webpack issue, so here is how my internals/webpack/webpack.base.babel.js looks like:
/**
* COMMON WEBPACK CONFIGURATION
*/
const path = require('path');
const webpack = require('webpack');
// Remove this line once the following warning goes away (it was meant for webpack loader authors not users):
// 'DeprecationWarning: loaderUtils.parseQuery() received a non-string value which can be problematic,
// see https://github.com/webpack/loader-utils/issues/56 parseQuery() will be replaced with getOptions()
// in the next major version of loader-utils.'
process.noDeprecation = true;
module.exports = options => ({
mode: options.mode,
entry: options.entry,
output: Object.assign(
{
// Compile into js/build.js
path: path.resolve(process.cwd(), 'build'),
publicPath: '/',
},
options.output,
), // Merge with env dependent settings
optimization: options.optimization,
module: {
rules: [
{
test: /\.(js|jsx)$/, // Transform all .js files required somewhere with Babel
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: options.babelQuery,
},
},
{
// Preprocess our own .css files
// This is the place to add your own loaders (e.g. sass/less etc.)
// for a list of loaders, see https://webpack.js.org/loaders/#styling
test: /\.scss$/,
exclude: /node_modules/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
// Preprocess 3rd party .css files located in node_modules
test: /\.css$/,
include: /node_modules/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(eot|otf|ttf|woff|woff2)$/,
use: 'file-loader',
},
{
test: /\.svg$/,
use: [
{
loader: 'svg-url-loader',
options: {
// Inline files smaller than 10 kB
limit: 10 * 1024,
noquotes: true,
},
},
],
},
{
test: /\.(jpg|png|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// Inline files smaller than 10 kB
limit: 10 * 1024,
},
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
enabled: false,
// NOTE: mozjpeg is disabled as it causes errors in some Linux environments
// Try enabling it in your environment by switching the config to:
// enabled: true,
// progressive: true,
},
gifsicle: {
interlaced: false,
},
optipng: {
optimizationLevel: 7,
},
pngquant: {
quality: '65-90',
speed: 4,
},
},
},
],
},
{
test: /\.html$/,
use: 'html-loader',
},
{
test: /\.(mp4|webm)$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
},
},
},
],
},
plugins: options.plugins.concat([
new webpack.ProvidePlugin({
// make fetch available
fetch: 'exports-loader?self.fetch!whatwg-fetch',
}),
// Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
// inside your code for any environment checks; UglifyJS will automatically
// drop any unreachable code.
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
},
}),
]),
resolve: {
modules: ['node_modules', 'app'],
extensions: ['.js', '.jsx', '.react.js'],
mainFields: ['browser', 'jsnext:main', 'main'],
},
devtool: options.devtool,
target: 'web', // Make web variables accessible to webpack, e.g. window
performance: options.performance || {},
});
I've tried solutions from several similar questions like this one.
Note:
I'm using this react boilerpalte.
Please help me unstuck folks.
After having looked at the docs a little closer here is how I got it to work...
Take a look here: https://github.com/react-boilerplate/react-boilerplate/tree/master/docs/js
They are using plop to autogenerate components containers etc...
So you simply do: npm run generate and follow the prompts...
I did and created a container called ProfilePage...
In my ProfilePage I imported the Header (the rest it simply auto-generated, the only thing I added was the import statement:
Here it is:
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
import { FormattedMessage } from 'react-intl';
import { createStructuredSelector } from 'reselect';
import { compose } from 'redux';
// Here is my import (notice no relative path needed)
import Header from 'components/Header';
import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer';
import makeSelectProfilePage from './selectors';
import reducer from './reducer';
import saga from './saga';
import messages from './messages';
/* eslint-disable react/prefer-stateless-function */
export class ProfilePage extends React.Component {
render() {
return (
<div>
<Helmet>
<title>ProfilePage</title>
<meta name="description" content="Description of ProfilePage" />
</Helmet>
<FormattedMessage {...messages.header} />
// Here is where I render it
<Header />
</div>
);
}
}
ProfilePage.propTypes = {
dispatch: PropTypes.func.isRequired,
};
const mapStateToProps = createStructuredSelector({
profilepage: makeSelectProfilePage(),
});
function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);
const withReducer = injectReducer({ key: 'profilePage', reducer });
const withSaga = injectSaga({ key: 'profilePage', saga });
export default compose(
withReducer,
withSaga,
withConnect,
)(ProfilePage);
That whole thing with the exception of the import Header from 'components/Header' and the rendering of header <Header /> just below <FormattedMessage {...messages.header} /> was auto-generated using plop
Then I simply imported ProfilePage in my App/index.js and added a route... Now when I navigate to localhost:3000/profile the ProfilePage shows up along with the Header...
Here is the App/index.js:
import React from 'react';
import { Helmet } from 'react-helmet';
import styled from 'styled-components';
import { Switch, Route } from 'react-router-dom';
import HomePage from 'containers/HomePage/Loadable';
import FeaturePage from 'containers/FeaturePage/Loadable';
import NotFoundPage from 'containers/NotFoundPage/Loadable';
import Header from 'components/Header';
import Footer from 'components/Footer';
// Here is where I import it...
import ProfiePage from 'containers/ProfilePage';
const AppWrapper = styled.div`
max-width: calc(768px + 16px * 2);
margin: 0 auto;
display: flex;
min-height: 100%;
padding: 0 16px;
flex-direction: column;
`;
export default function App() {
return (
<AppWrapper>
<Helmet
titleTemplate="%s - React.js Boilerplate"
defaultTitle="React.js Boilerplate"
>
<meta name="description" content="A React.js Boilerplate application" />
</Helmet>
<Header />
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/features" component={FeaturePage} />
// Here is the route I added...
<Route path="/profile" conmponent={ProfiePage} />
<Route component={NotFoundPage} />
</Switch>
<Footer />
</AppWrapper>
);
}
In conclusion, if you're going to use this boilerplate you absolutely need to go through their docs...
I'm developing a project using React/Redux and Node.js.
I added react-router-dom as a dependency into my project and configured the router like this:
import ...
const Router = () => (
<main>
<Switch>
<Route exact path='/' component={components.main}/>
<Route path='/caseDetail/:id' component={components.caseDetail}/>
<Route component={components.notFound}/>
</Switch>
</main>
)
export default Router
And setup my webpack like this
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const merge = require('webpack-merge')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const env = require('./src/env.js')
const TARGET = process.env.npm_lifecycle_event
process.env.BABEL_ENV = TARGET
const PATHS = {
app: path.join(__dirname, 'src'),
build: path.join(__dirname, 'build'),
assets: path.join(__dirname, 'assets')
}
const common = {
entry: [PATHS.app],
module: {
rules: [
{
test: /\.json$/,
loader: 'json-loader'
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
include: PATHS.app,
loaders: ['babel-loader']
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { modules: true } }],
include: /flexboxgrid/
},
{
test: /\.scss$/,
exclude: /flexboxgrid/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { modules: true } },
'sass-loader'
]
},
{
test: /\.jpe?g$|\.gif$|\.png$/i,
loader: 'url-loader?name=[name].[ext]'
},
{
test: /\.otf$|\.eot$|\.svg$|\.ttf|\.woff|\.woff2$/,
loader: 'url-loader?name=[name].[ext]'
}
]
},
resolve: {
extensions: ['.js', '.jsx'],
modules: ['node_modules', path.resolve(__dirname, './node_modules')],
mainFields: ['browser', 'web', 'browserify', 'main', 'style']
}
}
if (TARGET === 'start' || !TARGET) {
module.exports = merge(common, {
module: {
rules: [
{
test: /\.jsx?$/,
use: ['source-map-loader'],
enforce: 'pre'
}
]
},
devtool: 'inline-source-map',
devServer: {
contentBase: PATHS.build,
historyApiFallback: true,
hot: true,
inline: true,
progress: true,
stats: 'errors-only',
https: true,
host: env.host,
port: env.port,
overlay: {
errors: true
},
watchOptions: {
watch: true
}
},
plugins: [
new MiniCssExtractPlugin({ filename: 'assets/style.css' }),
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
template: PATHS.app + '/index.html',
inject: 'body'
})
],
output: {
path: PATHS.build,
filename: 'bundle.js'
}
})
}
if (TARGET === 'production') {
module.exports = merge(common, {
plugins: [
new MiniCssExtractPlugin({ filename: 'style.css' }),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: "'production'"
}
})
],
output: {
path: '/build',
filename: 'bundle.js'
}
})
}
My problem is
When I call this route /caseDetail/1 it works by using Link:
<Link to={{ pathname: `/caseDetail/` + caseStudy.id }}>
BUT, if I call directly by the browser, like https://localhost:3000/caseDetail/1
It fails, I get this message on console
Refused to apply style from 'https://localhost:3000/caseDetail/assets/style.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
0:12 GET https://localhost:3000/caseDetail/bundle.js net::ERR_ABORTED 404
0:1 Refused to execute script from 'https://localhost:3000/caseDetail/bundle.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
SOLVED
How to fix. Thanks to #soroush-chehresa
I added into my router file, the BrowserRouter like this:
import React from 'react'
import { Switch, Route, BrowserRouter } from 'react-router-dom'
import { components } from './loader'
const Router = () => (
<main>
<BrowserRouter>
<Switch>
<Route exact path='/' component={components.main}/>
<Route exact path='/caseDetail' component={components.caseDetail}/>
<Route path='/caseDetail/:id' component={components.caseDetail}/>
<Route component={components.notFound}/>
</Switch>
</BrowserRouter>
</main>
)
export default Router
And also added into my webpack.config.js on output
publicPath: '/'
If you are using react-router-redux wrap Router with ConnectedRouter:
import { ConnectedRouter } from 'react-router-redux';
import { createBrowserHistory } from 'history';
const Router = () => (
<main>
<ConnectedRouter history={createBrowserHistory()}>
<Switch>
<Route exact path='/' component={components.main}/>
<Route path='/caseDetail/:id' component= {components.caseDetail}/>
<Route component={components.notFound}/>
</Switch>
</ConnectedRouter>
</main>
);
Otherwise if you are using react-router-dom wrap Router with BrowserRouter:
import { BrowserRouter } from 'react-router-dom';
const Router = () => (
<main>
<BrowserRouter>
<Switch>
<Route exact path='/' component={components.main}/>
<Route path='/caseDetail/:id' component={components.caseDetail}/>
<Route component={components.notFound}/>
</Switch>
</BrowserRouter>
</main>
);
I'm new on React and I try to build my first application, for the route I used react-router and this is my App
class App extends Component {
render() {
return (
<Router history={browserHistory}>
<Route path='/' component={Container}>
<IndexRoute component={Home}/>
<Route path='/address' component={Address}>
<IndexRoute component={TwitterFeed} />
<Route path='instagram' component={Instagram} />
<Route path='query' component={Query} />
</Route>
<Route path='/about/:name' component={About} />
<Route path='/namedComponent' component={NamedComponents}>
<IndexRoute components={{ title: Title, subTitle: SubTitle }} />
</Route>
<Route path='*' component={NotFound} />
</Route>
</Router>
)
}
}
Everything work well but not this line <Route path='/about/:name' component={About} />, in fact if i try to write /address/12345 white page appears to me whitout error, someone can help me?
This is my webpack.config.js
'use strict';
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var webpack = require('webpack');
module.exports = {
entry: {
app: [
'./src/main.js'
]
},
output: {
path: __dirname + '/dist',
publicPath : '/',
filename: 'bundle.js'
},
plugins: process.env.NODE_ENV === 'production' ? [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin({ filename: '[name].css', disable: false, allChunks: true})
] : [],
devServer: {
contentBase: "./dist",
inline: true,
port: 3000,
},
//source map for js
devtool: 'source-map',
module: {
loaders: [
//babel ECMAScript 6 compiler
{
test: /\.js?$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.scss$/,
loaders: [ 'style-loader', 'css-loader?sourceMap', 'sass-loader?sourceMap' ] //sourceMap for debug css in devTools
/*loader: ExtractTextPlugin.extract({
fallback : 'style-loader', use : 'css-loader?sourceMap!sass-loader?sourceMap'
}) FOR PROD*/
}
]
},
}
And i use this script : webpack-dev-server --inline --content-base dist --history-api-fallback
In your component "Container" you should have children object.
You can have a better look on the snippet bellow.
import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
const BasicExample = () =>
<Router>
<div>
<ul>
<Route to="/" component={ComponentThatHaveChildren} />
<Route path="/path1/path2" component={ChildrenComponent} />
</ul>
</div>
</Router>;
const ComponentThatHaveChildren = props =>
<div>
<h1>Component That Have Children</h1>
<h2>{props.children}</h2>
</div>;
const ChildrenComponent = () =>
<div>
<h2>Children</h2>
</div>;
export default BasicExample;
I'm trying to achieve something like the following router structure in plain route objects.
const Demo = () => (
<Router history={hashHistory}>
<Route path="/" component={App}>
<Route path="fade" component={FadeDemo}>
<IndexRoute component={Lorem} />
<Route path="demo-1" component={Lorem} />
<Route path="demo-2" component={Lorem} />
<Route path="demo-3" component={Lorem} />
</Route>
My app router looks like this:
export const createRoutes = (store) => ({
path: '/',
component: CoreLayout,
indexRoute: Home,
childRoutes: [
CounterRoute(store)
]
})
So I want to add the FadeDemo transition container from the former JSX as a route without a path on my latter example. Is that possible?
EDIT:
That's my updated route index file, now I get can't match the '/counter' location:
import CoreLayout from '../layouts/CoreLayout/CoreLayout'
import Home from './Home'
import CounterRoute from './Counter'
import TransitionWrapper from './TransitionWrapper'
export const createRoutes = (store) => ({
path: '/',
component: CoreLayout,
indexRoute: Home,
childRoutes: [{
//path: 'fade',
component: TransitionWrapper,
childRoutes: [
CounterRoute(store)
]
}]
})
counter app index:
import { injectReducer } from '../../store/reducers'
export default (store) => ({
path: 'counter',
/* Async getComponent is only invoked when route matches */
getComponent (nextState, cb) {
/* Webpack - use 'require.ensure' to create a split point
and embed an async module loader (jsonp) when bundling */
require.ensure([], (require) => {
/* Webpack - use require callback to define
dependencies for bundling */
const Counter = require('./containers/CounterContainer').default
const reducer = require('./modules/counter').default
/* Add the reducer to the store on key 'counter' */
injectReducer(store, { key: 'counter', reducer })
/* Return getComponent */
cb(null, Counter)
/* Webpack named bundle */
}, 'counter')
}
})
TransitionWrapper
import React from 'react'
import { Link } from 'react-router'
import { RouteTransition } from 'react-router-transition'
const TransitionWrapper = (props) => (
<div>
<RouteTransition
component={"div"}
className="transition-wrapper"
pathname={this.props.location.pathname}
{...props.preset}
>
{this.props.children}
</RouteTransition>
</div>
)
export default TransitionWrapper
Here is described how you can achieve it.
export const createRoutes = (store) => ({
path: '/',
component: CoreLayout,
indexRoute: Home,
childRoutes: [
{
component: FadeDemo,
childRoutes: [
{
path: 'demo-1',
component: Lorem
},
{
path: 'demo-2',
component: Lorem
}
// ...
]
},
]
})