The issue is when I do npm run start-dev to run client side code, it works perfectly. But whenever I run npm run start to start up the server side. The Client side doesn't dispatch the content back to UI. Need help.
Server.js
'use strict';
import path from 'path';
import { Server } from 'http';
import Express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import routes from './routes';
import NotFoundPage from './components/NotFoundPage';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
const content = require('./Action/reducer');
// initialize the server and configure support for ejs templates
const app = new Express();
const server = new Server(app);
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// define the folder that will be used for static assets
app.use(Express.static(path.join(__dirname, 'static')));
// universal routing and rendering
app.get('*', (req, res) => {
match(
{ routes, location: req.url },
(err, redirectLocation, renderProps) => {
const store = createStore(content);
// in case of error display the error message
if (err) {
return res.status(500).send(err.message);
}
// in case of redirect propagate the redirect to the browser
if (redirectLocation) {
return res.redirect(302, redirectLocation.pathname + redirectLocation.search);
}
// generate the React markup for the current route
let markup;
if (renderProps) {
// if the current route matched we have renderProps
markup = renderToString(
<Provider store={store}>
<RouterContext {...renderProps}/>
</Provider>
);
} else {
// otherwise we can render a 404 page
markup = renderToString(<NotFoundPage/>);
res.status(404);
}
res.send(renderFullPage(markup))
// render the index template with the embedded React markup
// return res.render('index', { markup });
}
);
});
function renderFullPage(html) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="/img/favicon.ico">
<link rel="apple-touch-icon" sizes="76x76" href="assets/img/apple-icon.png">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>Chris</title>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />
<meta name="viewport" content="width=device-width" />
<link href="bootstrap3/css/bootstrap.css" rel="stylesheet" />
<link href="assets/css/gsdk.css" rel="stylesheet" />
<link href="assets/css/demo.css" rel="stylesheet" />
<!-- Font Awesome -->
<link href="bootstrap3/css/font-awesome.css" rel="stylesheet"/>
<link href='https://fonts.googleapis.com/css?family=Grand+Hotel' rel='stylesheet' type='text/css'/>
</head>
<body>
<div id="main">${html}</div>
<script src="../static/js/bundle.js"></script>
<script src="jquery/jquery-1.10.2.js" type="text/javascript"></script>
<script src="assets/js/jquery-ui-1.10.4.custom.min.js" type="text/javascript"></script>
<script src="bootstrap3/js/bootstrap.js" type="text/javascript"></script>
<script src="assets/js/gsdk-checkbox.js"></script>
<script src="assets/js/gsdk-radio.js"></script>
<script src="assets/js/gsdk-bootstrapswitch.js"></script>
<script src="assets/js/get-shit-done.js"></script>
<script src="assets/js/custom.js"></script>
</body>
</html>
`
}
// start the server
const port = process.env.PORT || 3000;
const env = process.env.NODE_ENV || 'production';
server.listen(port, err => {
if (err) {
return console.error(err);
}
console.info(`Server running on http://localhost:${port} [${env}]`);
});
Client:
'use strict';
import React from 'react';
import { render } from 'react-dom'
import { Router, browserHistory } from 'react-router';
import routes from './routes';
import Layout from './components/Layout';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
const reducer = require('./Action/reducer');
const store = createStore(reducer);
// import { createStore } from 'redux';
// const content = require('./Action/reducer');
// const store = createStore(content);
// import Layout from './components/Layout';
render(
<Provider store={store}>
<Router history={browserHistory} routes={routes} onUpdate={() => window.scrollTo(0, 0)}/>
</Provider>,
document.getElementById('main')
)
Layout.js
'use strict';
import React from 'react';
import { Link } from 'react-router';
// const actions = require('../Action/actions');
import * as actions from '../Action/actions';
import {connect} from 'react-redux';
class Layout extends React.Component {
handleSwitchLang(targetLang) {
this.props.switchLanguage(targetLang);
}
render() {
let switchLanguage = this.props.switchLanguage;
let content = this.props.content;
return (
some UI code
);
}
}
function mapStateToProps(state) {
return { content: state.content }
}
export default connect(mapStateToProps, actions)(Layout);
reducer.js
const action_types = require('./action_types');
import json from '../../lang.json';
const initialState = {
content: json.en // Loads default language content (en) as an initial state
};
const reducer = function (state = initialState, action) {
switch (action.type) {
case action_types.SWITCH_LANGUAGE:
return {
content: json[action.language]
};
default:
return state;
}
};
module.exports = reducer;
You can try out my code if you want. Here is my github project.
https://github.com/luznlun/samwebsite
clone the project. do npm install, npm runs start-dev to run client side(Working), npm run start(problem with dispatch).
Really appreciate the help
Related
I've created a SSR React app that loads data on the server and sends it to the client as html. The problem kicks in after the initial server request has been served and i try to switch to a different navigation link. The url changes to the correct path but the page itself breaks with a TypeError: Cannot read property 'length' or 'map' of undefined. I believe the fetching somehow is not working on the client side, because if i turn off JavaScript from the browser everything works just fine.
The App has four routes, Home, Movies (needs to fetch data), TvShows (needs to fetch data) and PageNotFound. Again, the problem occurs when for example I open the Home page and try to switch to Movies. However, if i open Movies or TvShows first everything loads correctly because of the initial request being served by the server. Here is my file structure:
And here's the content of some of my files:
index.js (Server.js)
import "#babel/polyfill";
import express from "express";
import { applyMiddleware, createStore } from "redux";
import Routes from "./client/Routes";
import { matchRoutes } from "react-router-config";
import renderer from "./helpers/renderer.js";
import thunk from "redux-thunk";
import reducers from "./reducers";
const compression = require("compression");
const app = express();
app.use(compression());
app.use(express.static("public")); //treats the public(client side) directory as public, available to the outside world
// This is fired every time the server side receives a request
app.get("*", (req, res) => {
// Create a new Redux store instance
const store = createStore(reducers, {}, applyMiddleware(thunk));
const promises = matchRoutes(Routes, req.path)
.map(({ route }) => {
return route.loadData ? route.loadData(store) : null;
})
.map((promise) => {
if (promise) {
return new Promise((resolve, reject) => {
promise.then(resolve).catch(resolve);
});
}
});
Promise.all(promises).then(() => {
// Send the rendered page back to the client
// Grab the initial state from our Redux store
const context = {};
//const finalState = store.getState();
const content = renderer(req, store, context);
if (context.notFound) {
res.status(404);
}
res.send(content);
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT);
client.js
import "#babel/polyfill";
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { Provider } from "react-redux";
import { renderRoutes } from "react-router-config";
import Routes from "./Routes";
import reducers from "../reducers";
// Grab the state from a global variable injected into the server-generated HTML
const preloadedState = window.__PRELOADED_STATE__;
// Create Redux store with initial state
const store = createStore(reducers, preloadedState, applyMiddleware(thunk));
ReactDOM.hydrate(
<Provider store={store}>
<BrowserRouter>
<div>{renderRoutes(Routes)}</div>
</BrowserRouter>
</Provider>,
document.querySelector("#root")
);
renderer.js
import React from "react";
import serialize from "serialize-javascript";
import { renderToString } from "react-dom/server";
import { StaticRouter } from "react-router-dom";
import { Provider } from "react-redux";
import Routes from "../client/Routes";
import { renderRoutes } from "react-router-config";
import { Helmet } from "react-helmet";
// Render the component to a string
export default (req, store, context) => {
const html = renderToString(
<Provider store={store}>
<StaticRouter location={req.path} context={context}>
<div>{renderRoutes(Routes)}</div>
</StaticRouter>
</Provider>
);
const helmet = Helmet.renderStatic();
return `
<!doctype html>
<html>
<head>
${helmet.title.toString()}
${helmet.meta.toString()}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
</head>
<body>
<div id="root">${html}</div>
<script>
// WARNING: See the following for security issues around embedding JSON in HTML:
// https://redux.js.org/recipes/server-rendering/#security-considerations
window.__PRELOADED_STATE__ = ${serialize(store.getState())}
</script>
<script src="/bundle.js"></script>
</body>
</html>
`;
};
Routes.js
import App from "./App";
import HomePage from "./pages/HomePage";
import MovieListPage from "./pages/MovieListPage";
import TvShowsPage from "./pages/TvShowsPage";
import NotFoundPage from "./pages/NotFoundPage";
//using spread operator for the components
//and loadData function(if available)
//because they are imported in object form now
export default [
{
...App, //no path added to App, meaning it will always be displayed on screen
routes: [
{
...HomePage,
path: "/",
exact: true,
},
{
...MovieListPage,
path: "/movies",
exact: true,
},
{
...TvShowsPage,
path: "/tvshows",
exact: true,
},
{
...NotFoundPage, //will be shown if react router can't match any of the defined routes
},
],
},
];
MovieListPage.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchMovies } from "../../actions";
import { Helmet } from "react-helmet";
class MovieListPage extends Component {
// Have state ready for both Movies and TvShows link clicks/direct requests
componentDidMount() {
if (this.props.movies) return;
this.props.fetchMovies();
}
renderData() {
return this.props.movies.results.map((movie) => {
return (
<div
key={movie.id}
className="card text-center m-3"
style={{ width: "15rem" }}
>
<img
className="card-img-top"
alt="..."
src={this.dynamicUrl(movie)}
/>
<div className="card-body">
<h5 className="card-title">{movie.title}</h5>
<p className="card-text font-weight-light">{movie.release_date}</p>
<a href={this.dynamicLink(movie)} className="btn btn-secondary">
TMDB
</a>
</div>
</div>
);
});
}
dynamicUrl(movie) {
let url = "https://image.tmdb.org/t/p/w200/" + movie.poster_path;
return url;
}
dynamicLink(movie) {
let link = "https://www.themoviedb.org/movie/" + movie.id;
return link;
}
head() {
return (
<Helmet>
<title>{`${this.props.movies.results.length} Movies Loaded`}</title>
<meta property="og:title" content="Movies" />
</Helmet>
);
}
render() {
return (
<div className="container">
{this.head()}
<div className="row">{this.renderData()}</div>
</div>
);
}
}
function mapStateToProps(state) {
return { movies: state.movies };
}
function loadData(store) {
return store.dispatch(fetchMovies());
}
//exporting the component and the loadData function (if present)
//in the form of an object(key:value pair)
// to avoid overlap of different loadData function imports in Routes
export default {
loadData,
component: connect(mapStateToProps, { fetchMovies })(MovieListPage),
};
I can't seem to figure out what is it that i'm missing.
I found the issue in renderer.js. I didn't provide the correct path for my client-side bundle.js in the html served by the server. Instead of <script src="/bundle.js"></script> it had to be <script src="/public/bundle.js"></script>.
I am using React Router. When i try to enter my interface by localhost:3000/Login; i am getting this error. "Invariant failed: Browser history needs a DOM". I googled everywhere but i couldn't find any solutions. I am sharing my codes, anyone to help??
my server.js file
import React from 'react';
import { StaticRouter } from 'react-router-dom';
import express from 'express';
import { renderToString } from 'react-dom/server';
import { Provider } from 'react-redux';
import serialize from 'serialize-javascript';
import App from '$/containers/app';
import setupStore from '$/state/store';
const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);
const server = express();
server
.disable('x-powered-by')
.use(express.static(process.env.RAZZLE_PUBLIC_DIR))
.get('/*', (req, res) => {
// Compile an initial state
const preloadedState = { counter: 0 };
// Create a new Redux store instance
const store = setupStore(preloadedState);
const context = {};
// Render the component to a string
const markup = renderToString(
<Provider store={store}>
<StaticRouter context={context} location={req.url}>
<App />
</StaticRouter>
</Provider>,
);
// Grab the initial state from our Redux store
const finalState = store.getState();
if (context.url) {
res.redirect(context.url);
} else {
res.status(200).send(
`<!doctype html>
<html lang="">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="utf-8" />
<title>Welcome to Razzle</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
${
assets.client.css
? `<link rel="stylesheet" href="${assets.client.css}">`
: ''
}
${
process.env.NODE_ENV === 'production'
? `<script src="${assets.client.js}" defer></script>`
: `<script src="${assets.client.js}" defer crossorigin></script>`
}
</head>
<body>
<div id="root">${markup}</div>
<script>
window.__PRELOADED_STATE__ = ${serialize(finalState)}
</script>
</body>
</html>`,
);
}
});
export default server;
my app.js file
import React from 'react';
import Home from '$/components/pages/home';
import Login from '$/components/pages/login';
import Logout from '$/components/pages/logout';
import '$/css/app.css';
const { Route, Switch, BrowserRouter } = require('react-router-dom');
const App = () => (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/login" component={Login} />
<Route exact path="/logout" component={Logout} />
</Switch>
</BrowserRouter>
);
export default App;
my client.js file
import React from 'react';
import { hydrate } from 'react-dom';
import { Provider } from 'react-redux';
import setupStore from '$/state/store';
import App from '$/containers/app';
const store = setupStore(window.__PRELOADED_STATE__);
const { BrowserRouter } = require('react-router-dom');
hydrate(
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>,
document.getElementById('root'),
);
if (module.hot) {
module.hot.accept('./containers/app', () => {
hydrate(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'),
);
});
}
I am new in server side rendering. I am trying to implement it like following but, it shows error.
index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root"></div>
</body>
</html>
index.js
import React from 'react';
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'
import rootReducer from './reducers'
import './index.css'
import Main from './Main'
const preloadedState = window.__PRELOADED_STATE__
delete window.__PRELOADED_STATE__
const store = createStore(
rootReducer,
preloadedState,
applyMiddleware(thunk)
);
ReactDOM.hydrate(
<Provider store={store}>
<Main />
</Provider>,
document.getElementById('root')
);
server.js
import express from 'express';
import open from 'open';
import rootReducer from '../src/reducers'
import Main from '../src/Main';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { renderToString } from 'react-dom/server'
var app = express();
const port = 4000;
app.use(express.static('dist'));
app.get('/', (req, res) => {
const store = createStore(rootReducer)
const html = renderToString(
<Provider store={store}>
<Main />
</Provider>
)
const preloadedState = store.getState();
res.send(renderFullPage(html, preloadedState))
});
function renderFullPage(html, preloadedState) {
return `
<!doctype html>
<html>
<body>
<div id="root">${html}</div>
<script>
window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g,'\\u003c')}
</script>
</body>
</html>
`
}
app.listen(port, function (err) {
if (err) {
console.log(err);
} else {
open('http://localhost:' + port);
}
})
When i try to run SSR it shows following error. I don't understand what i am doing wrong?
Warning: Expected server HTML to contain a matching <div> in <div>.
Any help would be greatly appreciated.
Edit:
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Content from './components/Content';
import PageNotFound from './components/PageNotFound'
import Movie from './components/Movie';
class Main extends Component {
render() {
return (
<div className="main">
<Router>
<Switch>
<Route exact path='/' component={Content} />
<Route path='/search/:query' component={Content} />
<Route path='/film/:id' component={Movie} />
<Route path='*' component={PageNotFound} />
</Switch>
</Router>
</div>
);
}
}
export default Main;
This is not duplicate of given link as i am using SSR. Then why should i use render instead of hydrate as i am using SSR?
I am trying to implement server side rendering with my react app that uses HMR and need some clarification on my issues. There seems to be miscommunication between the server and the frontend but I'm not entirely sure why that is.
When I start my server, the browser loads the client side index.html and I am able to edit my site with HMR working just fine. However, when I navigate to another page besides the homepage, and I refresh the browser, the server then renders the html markup in my handleRender function. The sites html markup appears, however there is no css and HMR doesn't work.
To summarize, on first load, it renders the app client side, but upon refresh, it renders the app server side. I'm thinking it might have to do with the way I am using router.use(express.static(clientDir)); but I'm not sure. Any clarification would be helpful!
Client side Index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Base Template</title>
<link rel="stylesheet" type="text/css" href="/static/bundle.css">
</head>
<body id="body">
<div class="root"></div>
<script src="/static/bundle.js"></script>
</body>
</html>
React App index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { AppContainer } from 'react-hot-loader'
import { BrowserRouter } from 'react-router-dom'
import App from './App'
const root = document.querySelector('.root');
// Wraps our App in AppContainer
const render = (Component) => {
ReactDOM.render(
<BrowserRouter>
<AppContainer>
<Component/>
</AppContainer>
</BrowserRouter>,
root
);
};
// Renders our application
render(App);
// Checks if there have been changes and accepts them.
// This activates HMR and refreshes modules in the browser
if (module.hot) {
module.hot.accept();
}
Express Server
import express from 'express'
import React from 'react'
import { renderToString } from 'react-dom/server'
import { StaticRouter } from 'react-router'
import { resolve } from 'path'
import routes from './routes'
// Imports our React App
import App from '../client/scripts/App'
// Create Express App
const app = express();
// Set the port for our server
app.set('port', process.env.PORT || 3000)
// Use routes to check if the environment is prod || dev
app.use(routes)
// Use handleRender for SSR
app.use(handleRender)
function handleRender(req, res) {
const context = {};
const html = renderToString(
<StaticRouter location={req.url} context={context} >
<App/>
</StaticRouter>
)
if (context.url) {
res.writeHead(301, {
Location: context.url
})
res.end()
} else {
res.write(`
<!doctype html>
<head>
<title>Redux Universal Example</title>
<link rel="stylesheet" type="text/css" href="/static/bundle.css">
</head>
<body>
<div id="app">${html}</div>
<script type="javascript" src="/static/bundle.js"></script>
</body>
`)
res.end()
}
}
export default app
Routes for server
import express, { Router } from 'express'
import { resolve } from 'path'
import { isDev } from '../utils'
const router = Router();
const clientDir = resolve(`${__dirname}/../../client`);
if (isDev()) {
const webpackDevMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')
const webpackConfig = require('../../../webpack.config')
const webpackHotMiddleware = require('webpack-hot-middleware')
const compiler = webpack(webpackConfig)
// This compiles our app using webpack
router.use(webpackDevMiddleware(compiler, {
publicPath: webpackConfig.output.publicPath,
noInfo: true
}))
// This connects our app to HMR using the middleware
router.use(webpackHotMiddleware(compiler))
}
router.use(express.static(clientDir));
export default router
I'm trying to render the following component on the server side as part of a universal/isomorphic app:
import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { GridLoader } from 'halogen';
import PostListItem from '../../components/PostListItem/PostListItem';
import { primary as color } from '../../colors';
import { changeSelectedPost, deletePostRequest } from '../../redux/modules/post';
export default connect()(class PostListView extends Component {
static propTypes = {
posts: ImmutablePropTypes.listOf(ImmutablePropTypes.contains({
name: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
slug: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
})).isRequired,
loading: PropTypes.boolean.isRequired,
dispatch: PropTypes.func.isRequired,
}
handleClick(post) {
this.props.dispatch(changeSelectedPost(post));
}
handleDelete(post) {
if (confirm('Do you want to delete this post')) { // eslint-disable-line
this.props.dispatch(deletePostRequest(post));
}
}
render() {
if (typeof window !== 'undefined' && this.props.loading) {
return (
<div className="container">
<GridLoader color={color} />
</div>
);
}
return (
<div className="listView">
{
this.props.posts.toSeq().map((post, i, arr) => (
<PostListItem
post={post}
key={i}
onClick={this.handleClick.bind(this, post)}
onDelete={this.handleDelete.bind(this, post)}
/>
))
}
</div>
);
}
});
but I receive the error:
module.exports = document.createElement('div').style;
^
ReferenceError: document is not defined
Adding the if block seems to have tripped up the app (the app rendered perfectly before, both server and client side). I'm most likely missing something quite obvious, knowing my track record :P. Any suggestions? :)
UPDATE: File that handles server-side rendering and the rest of the app on the server side:
// server/server.js
'use-strict';
import path from 'path';
import bodyParser from 'body-parser';
import Express from 'express';
import React from 'react';
import webpack from 'webpack';
import { fromJS } from 'immutable';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import { match, RouterContext } from 'react-router';
import { Model } from 'objection';
import { Provider } from 'react-redux';
import { renderToString } from 'react-dom/server';
import config from '../webpack.config.dev';
import routes from '../shared/routes';
import configureStore from '../shared/redux/configureStore';
import assets from './assets';
import db from './db';
import posts from './routes/post.routes';
import Post from './models/post';
import serverConfig from './serverConfig';
// Initialize the Express App
const app = new Express();
Model.knex(db);
if (process.env.NODE_ENV !== 'production') {
const compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }));
app.use(webpackHotMiddleware(compiler));
}
// Apply body Parser and server public assets and routes
app.use(bodyParser.json({ limit: '20mb' }));
app.use(bodyParser.urlencoded({ limit: '20mb', extended: false }));
if (process.env.NODE_ENV !== 'production') {
app.use(Express.static(path.resolve(__dirname, '../static')));
}
function getFilename() {
if (process.env.NODE_ENV !== 'production') {
return '"/dist/bundle.js"';
}
return `"/dist/${assets}"`;
}
app.use('/api', posts);
// Render Initial HTML
const renderFullPage = (html, initState, jsFile) => {
return `
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/styles/normalize.css">
<link rel="stylesheet" href="/styles/skeleton.css">
<title>CeresShop</title>
</head>
<div id="root">${html}</div>
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(initState)};
</script>
<script src=${jsFile}></script>
</body>
</html>
`;
};
// Server Side Rendering based on routes matched by React-router.
app.use((req, res) => {
match({ routes, location: req.url }, (err, redirectLocation, renderProps) => {
if (err) {
return res.status(500).end('Internal server error');
}
if (!renderProps) {
return res.status(404).end('Not found!');
}
const initialState = fromJS({
postReducer: {
posts: [],
post: {},
loading: false,
},
route: {
locationBeforeTransitions: null,
},
});
async function loadData() {
if (req.url.includes('post')) {
try {
const newSlug = req.url.substring(5).split('-');
const newId = newSlug[newSlug.length - 1];
const newPost = await Post.query().where('id', newId);
const toBeProcessed = JSON.stringify(newPost[0]);
return initialState.setIn(['postReducer', 'post'], fromJS(JSON.parse(toBeProcessed)));
} catch (error) {
console.log(error);
return initialState;
}
}
try {
const newPosts = await Post.query();
newPosts.sort((a, b) => b.dateadded - a.dateadded);
const toBeProcessed = JSON.stringify(newPosts);
return initialState.setIn(['postReducer', 'posts'], fromJS(JSON.parse(toBeProcessed)));
} catch (error) {
console.log(error);
return initialState;
}
}
loadData().then((currentState) => {
const store = configureStore(currentState);
const createElement = (Component, props) => (
<Component
{...props}
radiumConfig={{ userAgent: req.headers['user-agent'] }}
/>
);
const initialView = renderToString(
<Provider store={store}>
<RouterContext {...renderProps} createElement={createElement} />
</Provider>
);
const finalState = store.getState().toJS();
res.status(200).end(renderFullPage(initialView, finalState, getFilename()));
}).catch(err1 => {
console.log(err1);
res.end(renderFullPage(`Error: ${err1}`, {}, getFilename()));
});
});
});
// start app
app.listen(serverConfig.port, (error) => {
if (!error) {
console.log(`DAT SERVER is running on port: ${serverConfig.port}!`); // eslint-disable-line
}
});
export default app;
If you have a component that is being rendered server-side that requires another component which will only will be rendered in the client, it will still attempt to export that module on the server. You can either check if document exists, or require the module inline.
module.exports = typeof document !== 'undefined' ? document.createElement('div').style : null
// or
componentDidMount() {
// this is only called in the client
require('./clientComponent')
}