jsx passed to react-dom/server renderToString - reactjs

I'm trying to understand react-dom/server.
I have a react application, using express as the server.
I have an express route like so :
var server = app.listen(3000);
app.get('/test', (req, res) => {
const context = {}
const html = ReactDOMServer.renderToString(
<h1>foo</h1>
)
if (context.url) {
res.writeHead(302, {
Location: context.url
})
res.end()
} else {
res.write(html)
res.end()
}
});
If I run the server file with node app-server, I get this error :
<h1>adasd</h1>
^
SyntaxError: Unexpected token <
I passed JSX to renderToString based on the example there :
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/StaticRouter.md
How can I make the express server file processed this JSX code ?
For the client side, I use webpack with the babel loader, and it works fine.

Thanks to #azium.
Solution was to first convert the ES6/JSX file with babel-cli .
I used this babel-cli command :
npx babel --no-babelrc script.js --out-file script-compiled.js --presets=es2015,react
npx is used to "execute npm package binaries"

Related

Cypress throws Webpack compilation error when trying to run tests inside a reactjs app

Error: Webpack Compilation Error ./client/src/containers/Login.jsx
Module not found: Error: Can't resolve 'Src/api' in
'/Users/user/Documents/product-bots/client/src/containers' resolve
'Src/api' in
'/Users/sathish/Documents/product-bots/client/src/containers' Parsed
request is a module using description file:
/Users/sathish/Documents/product-bots/client/package.json (relative
path: ./src/containers)
I am trying to set up and run cypress inside a react.js app. There is an import statement in the app that says import api from 'Src/api. Cypress fails at this point and throws the above error.
Can anyone please help me to resolve this issue?
Below is the test code
import Login from '../../../client/src/containers/Login'
describe('Login', () => {
it('Tries login', () => {
mount(<Login />)
cy.get('.login-form').should('exist')
})
})
The above code calls login.jsx which has the 'Src/api' import.
jsconfig.js:
"paths": {"Src/*": ["src/*"] }
webpack.config.js:
alias: { Src: path.resolve(Paths.srcDir)

Error: Couldn't find a style target. It started to appear after SSR with React implementation. How to fix?

I had a CSR app and now implemented Server-Side Rendering (SSR) with React, and get this error
Error: Couldn't find a style target
We used reactDOMserver, react-app-wire, with webpack-override file
See webpack configs below:
const { resolve } = require("path");
require("ignore-styles");
module.exports = {
webpack: function (config) {
config.entry = resolve(dirname, "../client/src/ssr");
config.optimization = undefined;
config.output = {
filename: "ssr.js",
globalObject: 'this',
libraryTarget: "commonjs",
path: resolve(dirname, "../client/build"),
};
return config;
},
};
from server
app.get("^/$", (req, res) => {
Object.assign(global, {
navigation: {},
window: ssr.getWindow(),
navigator: {
userAgent: req.headers["user-agent"],
},
global: {},
document:ssr.ssrDocument
});
const App = require("./build/ssr");
const context = {};
let app = App.default(req.url, context);
then res,render app
AND SSR JS FILE
ssr.js file :
import { renderToString } from "react-dom/server";
for renderToString(
....
<App/>
....
)
And received Error: Couldn't find a style target
I had a CSR app and now implemented Server-Side Rendering (SSR) with React, and get this error
Error: Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid
Get this error when I run the server and try to access the page.
We also tried other approaches with these loaders:
css-loader, sass loader and style-loader and even with isomorphic-style-loader
They led to the error with No PostCSS config and after fixing this by adding to the project postcss.config.js file AND AFTER we received same error about style target and additional error with antd
Error: No PostCSS Config found in: D:_PROJECT....\client\node_modules\antd\dist
Tech sack in use: nodejs, reactjs, antdesign, babel and webpack.
I already tried a lot of solutions from StackOverflow and other sources, nothing helped!
Please, help me to solve it.
Thank you!
I think you cannot use style-loader server side.
Find the loader like this: https://github.com/codebandits/react-app-rewire-css-modules/blob/master/index.js just use style-loader instead, and remove it.. or switch to razzle

ngrok does not work with react-router when deep links are explicitly defined

Context: I'm opening up my React dev environment to external hits using a paid version of the tool ngrok -- I am running WebPack 4.0 with a devServer.
I go to my app's main page:
https://my-example-domain.ngrok.io
Loads fine.
I click one of the links on my app's main page, The router works well and I see the content for:
https://my-example-domain.ngrok.io/my-sub-page
However, if I refresh at this point, ngrok gives out a 404:
Cannot GET /my-sub-page
The reason I suspect is that in a SPA, the internal app's URLs are processed by the front end in React-Router and externally, there is no resource called /my-sub-page
Is there a way to force this to work?
The problem is that the local server doesn't know about your spa routes(react-router). To fix this, you have to create a simple server that will serve your static build and have a fallback route that will enable spa-routing.
Example in express.js:
import express from 'express';
import path from 'path';
// ... other imports
const app = express();
app.use(express.static('build'));
/*
... some route handlers
*/
// * Unknown endpoint
app.use("/*", (_req, res) => {
res.sendFile(path.join(__dirname, "../build/index.html"), (err) => {
if (err) {
res.status(500).send(err);
}
});
});
app.listen(process.env.PORT || 3000 , ()=>{
console.log('server running')
})
This resolved it -- in webpack.config.js
devServer: {
...
...
historyApiFallback: true, // <- inserting this resolved the issue.
}

React CRA with SSR configuration throws 404 when using the resulting html

I have a react application built using CRA with the default configurations. I wanted to make SSR, so I followed an article something similar to this https://www.vairix.com/tech-blog/server-side-rendering-ssr-of-create-react-app-cra-app-in-2020
Next, I wanted to inline the JS and CSS so that I could hit the URL and copy the resultant HTML page and then use it where ever I want.
For that, I used react-app-rewired plugin which works now I can see the HTML with inline CSS and JS.
The issue is when I copy the generated HTML and save it as .html, and when I open the page, it returns 404 error.
I am trying to copy the HTML that is produced and then use them as individual components in a completely different application.
Am I missing something?
config-overrides.js for react app rewired
const HtmlWebpackPlugin = require("html-webpack-plugin");
const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin");
const HTMLInlineCSSWebpackPlugin = require("html-inline-css-webpack-plugin").default;
module.exports = {
webpack: function(config, env) {
if (env === "production") {
config.plugins.push(new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/.*/]));
config.plugins.push(new HTMLInlineCSSWebpackPlugin());
}
return config;
}
};
server.js for SSR
import express from "express";
import path from "path";
import renderer from "./react-renderer";
const app = express();
const PORT = 8000;
const routes = ["/custom1/:id","/custom2/:id","/custom3/:id"];
app.get("/*", renderer(routes));
app.use(express.static(path.resolve(__dirname, "../build")));
app.use(express.static(path.resolve(__dirname, "../public")));
app.listen(PORT, () => {
console.log(`App running in the port ${PORT}`);
});
404 is the HTTP code for not found: I don't think the problem is in opening the page but in finding it.
It seems the first route you added in your server is catching all the requests:
app.get("/*", renderer(routes));
you could try moving it as last route
app.use(express.static(path.resolve(__dirname, "../build")));
app.use(express.static(path.resolve(__dirname, "../public")));
app.get("/*", renderer(routes));
More than this you didn't said where you copied the resulting page, I hope in ../build or in ../public directories, otherwise I'm not surprised you can't GET it.

Heroku redirect Next.js React client app http to https

I have an express server deployed on Heroku: https://server.mydomain.com
and a Next.js React app also deployed on Heroku: https://app.mydomain.com
Both have their SSL certificates automatically configured by Heroku, and when I visit the https domains, they work as expected.
The problem I have is that when I visit http://app.mydomain.com, it does not redirect to https://app.mydomain.com.
All the solutions I've found online point to forcing SSL on the server:
this popular question says to check for the x-forwarded-proto value:
/* At the top, with other redirect methods before other routes */
app.get('*',function(req,res,next){
if(req.headers['x-forwarded-proto']!='https')
res.redirect('https://app.mydomain.com'+req.url)
else
next() /* Continue to other routes if we're not redirecting */
})
and others suggest using a package like express-sslify or heroku-ssl-redirect.
These solutions work fine for the server requests, but loading a React client page does not necessarily trigger app.get(). Obviously, a React client can run independently of a server.
So the question is: How does someone force https for a subdomain Next.js React client app on Heroku? Without using express server methods?
I do this in one of my production applications.
We prepare the next app object and init an express server. This is done in the server.js file. You can read more about it in the docs about a custom server.
Next.js also has an example in their github in the examples folder about a custom express server. It's here.
const express = require('express');
const next = require('next');
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app
.prepare()
.then(() => {
const server = express();
server.use((req, res, next) => {
const hostname = req.hostname === 'www.app.domain.com' ? 'app.domain.com' : req.hostname;
if (req.headers['x-forwarded-proto'] === 'http' || req.hostname === 'www.app.domain.com') {
res.redirect(301, `https://${hostname}${req.url}`);
return;
}
res.setHeader('strict-transport-security', 'max-age=31536000; includeSubDomains; preload');
next();
});
server.get('*', (req, res) => handle(req, res));
server.listen(
4242,
error => {
if (error) throw error;
console.error('Listening on port 4242');
}
);
})
.catch(error => {
console.error(error);
process.exit(1);
});
As for deploying to Heroku you should be able to just customize the npm start script to start nextjs like so:
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
Heroku also runs npm run build automatically so it should build the app for you.
Heroku does not currently "offer out of the box" functionality to force the use of https for node apps.
However, with the release of Nextjs v12 you can accomplish this without having to setup a custom server and use middleware instead.
See this answer for example code and advantages of middleware vs custom server.
I also published a npm package to handle this:
import sslRedirect from 'next-ssl-redirect-middleware';
export default sslRedirect({});

Resources