Issue After Upgrading Node.js, Docker, and NPM – React App Reload Fails with 401
Environment Details:
I have recently upgraded to the following:
Node.js: v20.0.0
Docker: v20.0.0
NPM: v9.0.0
Additionally, I have updated all dependencies to ensure compatibility with Node.js v20 and removed deprecated ones. Necessary modifications were made to React components and test files to align with the upgraded dependencies.
How the App Works
-
I have two server files:
server.js (for production) dev-server.js (for development) -
For development, I start dev-server.js locally.
-
The app loads a static HTML page in the browser, which contains a
form with all the static data required for the API call (including
the auth/SIF token). -
When I submit the form:
- A new tab is opened at http://localhost:3000/.
- The request is sent to the running server, which returns an HTML page
containing the submitted API data, static files, and Webpack-bundled
assets. - The React app mounts successfully on the browser.
Issue
The React app loads perfectly on the initial form submission.
However, when I reload the page on any route, http://localhost:3000/ is called and fails with a 401 (Unauthorized) error.
Observations
- After reload, form data becomes undefined.
- The http://localhost:3000/ request should be a POST call, but after
reload, it becomes a GET request. - Reloading http://localhost:3000/static works fine and returns the
expected HTML from the server without any issues. - If there is any syntax error in the React app after it loads, a POST
request to http://localhost:3000/ is triggered even though the .jsx
file has issues. - Cookies are getting deleted unexpectedly.
What I Have Tried So Far
- Ensured that cookies are set properly.
They are no getting set after reload
- Checked if authentication tokens persist across reloads.
No they do not persist after reload
- Verified that form data is available at the time of reload.
No after reload the form data comes undefined.
- Checked browser dev tools to inspect network requests and cookies.
They disappear after reload.
Expected Behavior
-
The app should persist the authentication state after a reload.
-
Form data should not be lost when reloading the page.
-
The request should remain a POST request instead of switching to GET.
-
Cookies should not be deleted automatically.
Sharing you my code with for your reference:
main.js
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<Router>
<Layout>
<Routes>
{indexRoute}
<Route path="/resource/:resource_id" element={<ResourceLoader />} />
{promptRoute}
</Routes>
</Layout>
</Router>
</Provider>
);
dev-server and server.js
app.static("node_modules/dist");
// Launch our UI APP
app.use(async (ctx) => {
const items_script_url = config.urls.items;
ctx.body = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
<title>React APP</title>
<link rel="shortcut icon" href="static/favicon.ico">
<link rel="stylesheet" href="static/styles.css">
<script>
window.WebComponents = window.WebComponents || {};
window.WebComponents.root = 'node_modules/@webcomponents/webcomponentsjs/';
</script>
<script type="application/javascript">
window._LTI_PARAMS_ = ${JSON.stringify(ctx.request.body)};
</script>
<script src="${items_script_url}"></script>
</head>
<body>
<div id="root"></div>
<script type="application/javascript" src="static/vendor.js?version=1.0"></script>
<script type="application/javascript" src="static/app.js?version=1.0"></script>
</body>
</html>
`;
});
webpack.config.js
import webpack from 'webpack';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import ESLintPlugin from 'eslint-webpack-plugin';
import config from '../config';
import debug from 'debug';
const _debug = debug('app:webpack:config');
const paths = config.utils_paths;
const { __DEV, __PROD, __TEST } = config.globals;
_debug('Create configuration.');
_debug('_DEV', __DEV);
_debug('PROD', __PROD);
_debug('TEST', __TEST);
const webpackConfig = {
name: 'client',
target: 'web',
devtool: __PROD ? false : config.compiler_devtool,
resolve: {
modules: [paths.base(config.dir_client), 'node_modules'],
extensions: ['.js', '.jsx', '.json'],
},
module: {},
};
// ------------------------------------
// Entry Points
// ------------------------------------
const APP_ENTRY_PATH = `${paths.base(config.dir_client)}/main.js`;
webpackConfig.entry = {
app: __DEV
? [
APP_ENTRY_PATH,
`webpack-hot-middleware/client?path=${config.compiler_public_path}__webpack_hmr`,
]
: [APP_ENTRY_PATH],
vendor: config.compiler_vendor,
};
// ------------------------------------
// Bundle Output
// ------------------------------------
webpackConfig.output = {
filename: '[name].js',
path: paths.base(config.dir_dist),
publicPath: config.compiler_public_path,
};
// ------------------------------------
// Plugins
// ------------------------------------
webpackConfig.plugins = [new webpack.DefinePlugin(config.globals)];
if (__DEV) {
_debug('Enable plugins for live development (HMR, NoErrors).');
webpackConfig.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({ filename: 'styles.css' }),
new ReactRefreshWebpackPlugin()
);
} else if (__PROD) {
_debug('Enable plugins for production.');
webpackConfig.plugins.push(
new HtmlWebpackPlugin({
template: 'node_modules/dist/button.html',
hash: false,
filename: 'button.html',
inject: 'head',
minify: { collapseWhitespace: true },
}),
new HtmlWebpackPlugin({
template: 'node_modules/dist/loader.html',
hash: false,
filename: 'loader.html',
inject: 'head',
minify: { collapseWhitespace: true },
}),
new MiniCssExtractPlugin({ filename: 'styles.css' })
);
}
// ------------------------------------
// Integrated ESLint Plugin
// ------------------------------------
if (__DEV || __PROD) {
webpackConfig.plugins.push(
new ESLintPlugin({
context: paths.base(config.dir_client),
extensions: ['js', 'jsx'],
emitWarning: __DEV,
failOnError: __PROD,
overrideConfigFile: paths.base('.eslintrc'),
})
);
}
// ------------------------------------
// Loaders
// ------------------------------------
webpackConfig.module.rules = [
{
test: /.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets: ['@babel/preset-env', '@babel/preset-react'],
plugins: [
'@babel/plugin-transform-runtime',
...(__DEV ? ['@babel/plugin-transform-react-jsx-source'] : []),
...(__PROD
? [
'babel-plugin-transform-react-remove-prop-types',
'@babel/plugin-transform-react-constant-elements',
]
: []),
],
},
},
},
{
test: /.json$/,
type: 'javascript/auto',
use: 'json-loader',
},
{
test: /.css$/,
include: /client/,
sideEffects: true,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
},
{
test: /.(woff2?|otf|ttf|eot|svg)$/,
type: 'asset/resource',
generator: {
filename: 'fonts/[name][ext][query]',
},
},
{
test: /.(png|jpg)$/,
type: 'asset/inline',
},
];
export default webpackConfig;
Below are the package.json dependencies:
{
"dependencies": {
"@babel/plugin-transform-runtime": "^7.2.0",
"@babel/runtime": "^7.12.5",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
"@webcomponents/webcomponentsjs": "^2.8.0",
"eslint": "^8.57.1",
"fs": "0.0.2",
"history": "2.1.2",
"jquery": "^3.7.1",
"js-cookie": "2.1.3",
"jsdom": "^26.0.0",
"koa-connect": "^2.1.0",
"lodash": "4.17.21",
"node-fetch": "^3.3.2",
"postcss": "^8.5.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-redux": "^8.1.3",
"react-router-dom": "^7.1.5",
"react-router-redux": "^4.0.8",
"redux": "^4.2.1",
"redux-rest-resource": "^0.29.3",
"redux-thunk": "^2.4.2",
"resolve-url-loader": "^5.0.0",
"uninstall": "^0.0.0",
"validator": "^13.12.0",
"webpack-dev-middleware": "^7.4.2",
"webpack-hot-middleware": "^2.26.1",
"yargs": "3.32.0"
},
"devDependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/eslint-parser": "^7.26.5",
"@babel/node": "^7.26.0",
"@babel/plugin-transform-react-constant-elements": "^7.25.9",
"@babel/plugin-transform-react-jsx-source": "^7.25.9",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"@babel/register": "^7.25.9",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"autoprefixer": "^10.4.20",
"ava": "^6.2.0",
"babel-loader": "^9.2.1",
"babel-plugin-react-transform": "2.0.2",
"babel-plugin-rewire": "^1.2.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"babel-watch": "^7.8.1",
"better-npm-run": "0.0.7",
"css-loader": "^7.1.2",
"cssnano": "^7.0.6",
"deep-freeze-es6": "1.0.1",
"eslint-config-standard": "^17.1.0",
"eslint-config-standard-react": "^13.0.0",
"eslint-plugin-babel": "^5.3.1",
"eslint-plugin-flowtype": "^8.0.3",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-promise": "^6.6.0",
"eslint-plugin-react": "^7.37.4",
"eslint-webpack-plugin": "4.2.0",
"expect": "1.20.2",
"fs-extra": "0.26.7",
"html-webpack-plugin": "^5.6.3",
"imports-loader": "^5.0.0",
"jest": "^29.7.0",
"json-loader": "0.5.4",
"loader-utils": "^1.4.2",
"mini-css-extract-plugin": "2.9.2",
"nodemon": "^3.1.9",
"nyc": "^17.1.0",
"postcss-css-variables": "^0.19.0",
"postcss-import": "^16.1.0",
"postcss-loader": "^8.1.1",
"postcss-mixins": "^11.0.3",
"postcss-nesting": "^13.0.1",
"postcss-simple-vars": "^7.0.1",
"react-refresh": "^0.16.0",
"react-transform-catch-errors": "1.0.2",
"react-transform-hmr": "1.0.4",
"redux-mock-store": "1.2.0",
"rimraf": "2.5.4",
"webpack": "^5.97.1",
"webpack-cli": "^6.0.1",
"xo": "^0.60.0"
}
}
Any insights or suggestions would be greatly appreciated!
