React router: adding dynamic URL - reactjs

I am using React Router Dom 4.2.2
Currently I have one Router as follows:
import React from 'react';
import {BrowserRouter, Route, Switch, Link, NavLink} from 'react-router-dom';
import ExpensesDashboardPage from "../components/ExpensesDashboardPage";
import AddExpensePage from "../components/AddExpensePage";
import EditExpensePage from "../components/EditExpensePage";
import HelpPage from "../components/HelpPage";
import NotFoundPage from "../components/NotFoundPage";
import Header from "../components/Header";
const AppRouter = () => (
<BrowserRouter>
<div>
<Header/>
<Switch>
<Route path='/' component={ExpensesDashboardPage} exact={true}/>
<Route path='/create' component={AddExpensePage}/>
<Route path='/edit/' component={EditExpensePage}/>
<Route path='/help' component={HelpPage}/>
<Route component={NotFoundPage}/>
</Switch>
</div>
</BrowserRouter>
);
export default AppRouter;
And I have tested that we can navigate to each one of the Route.
Then I thought about using a dynamic URL as follows:
<Route path='/edit/:id' component={EditExpensePage}/>
So then the component which we should render is:
import React from 'react';
const EditExpensePage = (props) => {
console.log(props);
return (
<div>
This is the Edit Expenses page, enjoy!
</div>
);
};
export default EditExpensePage;
The question here is the following:
Why if we go to the following URL: http://localhost:8080/edit/33, the console outputs:
GET http://localhost:8080/edit/bundle.js 404 (Not Found)
33:1 Refused to execute script from 'http://localhost:8080/edit/bundle.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
I would expect to see the page being loaded and into the match props, a params Object with the id: 33
I have also read
How to achieve Dynamic routing in React Router 4?
Thank you for your help!

I also had the same issue. My best bet you are watching the React Udemy tutorial of Andrew Mead. I tried the solution as provided by #mani.saffarnia. Didn't Work Out.
Here is how I got to make it work:
Setup the property public path as publicPath: '/'in your webpack.config.js in output block and devserver block.
Also change the default webpack server port from 8080 to 8082(any random unused port) in webpack dev server to ensure there is no caching.
Deleted Node Modules.
Clear npm cache using npm cache clean --force
Delete Public Folder and reinstalled node modules and created a fresh public folder to serve index.html and bundle.js
Finally run dev server for a working output
Here's my final webpack config for the fixed issue.
const path = require('path');
module.exports = {
entry: './src/app.js',
output:{
path: path.resolve(__dirname, 'public'),
filename: 'bundle.js',
publicPath: '/'
},
module:{
rules:[
{
loader:'babel-loader',
test: /\.js$/,
exclude: /node_modules/
},
{
test:/\.s?css$/,
use:[
'style-loader',
'css-loader',
'sass-loader'
]
}
]
},
devtool:'inline-source-map',
devServer:{
contentBase : path.resolve(__dirname, 'public'),
historyApiFallback: true,
publicPath: '/',
port: 8082
}
};

You didn't do anything wrong in your code. I know the tutorial that you are watching and I had the same problem. I just deleted my "public" and "node_modules" folders. after that, I created a new public folder(with a new index.html inside it), and also used "npm install" to install all dependencies and create node_modules again. It worked for me and I hope it works for you too.

In your index.html file.
does your script import say:
<script src="./bundle.js"></script>
if so. remove the "." in from of the "/". So the script import is
<script src="/bundle.js"></script>
This worked for me

Related

Error in react-router-dom v6.4 not rendering component -> get <URL> not found

Starting my app on --> http://localhost:8000
I created a component called "NavBar", and in this component I created a Link to "/home".
In "/home" Im just rendering the following text --> "Test React Router Dom"
On "NavBar" component, clicking on Link to "/home" it work, but when I try to refresh the page on "/home" or just typing URL: "http://localhost:8000/home" it fails.
"react": "^18.2.0"
"react-dom": "^18.2.0"
"react-router-dom": "^6.4.1"
"typescript": "^4.8.4",
Error -->
// App.tsx
import { NavBar } from './components/NavBar'
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
export const App = () => {
return (
<div>
<Router>
<Routes>
<Route path="/" element={<NavBar />} />
<Route path="/home" element={<h1>Test React Router Dom</h1>} />
</Routes>
</Router>
</div>
)
}
// NavBar.tsx
import './navBar.style.scss'
import { Link } from 'react-router-dom'
export const NavBar = () => {
return (
<>
<Link to={'/home'}>Link to Home</Link>
</>
)
}
//index.tsx
import { createRoot } from 'react-dom/client'
import { App } from './App'
const container = document.getElementById('root') as HTMLElement
const root = createRoot(container)
root.render(<App />)
This will be because your server needs to support HTML5 push state. When you navigate by clicking a link that's using the HTML5 history API (which react-router uses under the hood), its only changing the URL in the address bar -- it doesnt actually try to fetch that page from the server. However when you refresh or hit the URL from the outside of the site, the browser will simply try to load that URL from the server -- there is no client side code even loaded to use the history API.
Your server needs a wildcard route configured such that any and all paths serve your index.html. Then, your JS etc will load, read from the URL, and display the desired page.
If it's an express server you'd need something like this -- but the exact details really depend on your server technology and setup:
app.get('*', function(request, response) {
response.sendfile(__dirname + '/public/index.html');
});
I found a solution, Im using WebPack and I added the following config on Development mode -->
devServer: {
historyApiFallback: true,
}
But I don't understand how "historyApiFallback" works.
Will I need to add some another configuration on my Production mode to prevent the same error?
// webpack.prod.js
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
mode: 'production',
devtool: 'source-map',
devServer: {
historyApiFallback: true,
},
optimization: {
minimizer: [new CssMinimizerPlugin(), new TerserPlugin()],
},
plugins: [],
}

React SSR alternatives

I am currently working on a project that someone else built and I was asked to implement server side rendering, it's a huge project and it uses a custom routing system based on a "builder JSON" taken by a main component that selects which components to render based on the route, it is meant to keep the app dynamic and to adjust to the needs of several customers.
I have been checking everywhere trying to find an answer but im new to SSR and it's a big challenge.
I am currently testing an approach using express that looks like this:
import 'babel-polyfill';
import express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { StaticRouter } from 'react-router';
import bodyParser from 'body-parser';
import { App } from '../src/App';
const app = express();
const PORT = process.env.PORT || 8000;
app.use(bodyParser.json());
app.use(express.static('ssrBuild'))
app.get('*', (req, res) => {
const context = {}
const content = ReactDOMServer.renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
const html = `
<html>
<head>
</head>
<body>
<div id="root">
${content}
</div>
</body>
</html>
`;
res.send(html);
});
app.listen(PORT, () => {
console.log(`App running on port ${PORT}`);
});
The problem I am currently having is that the App component calls the "complex routing component" from another repository in the node_modules (also build by them), my webpack config is taking the App component and finding jsx, which cant obviouly be run in the server.
Webpack config:
const path = require('path');
const webpackNodeExternals = require('webpack-node-externals');
module.exports = {
target: 'node',
entry: {
server: './ssr/server.js',
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, '..', 'ssrBuild'),
publicPath: '/ssrBuild'
},
module: {
rules: [
{
test: /\js$/,
loader: 'babel-loader',
exclude: '/node_modules/',
options: {
presets: [
'#babel/react',
['#babel/preset-env', {
targets: { browsers: ['last 2 versions'] }
}]
],
plugins: [
['#babel/plugin-proposal-decorators', { 'legacy': true }],
'#babel/plugin-proposal-class-properties',
'#babel/plugin-syntax-function-bind',
'#babel/plugin-transform-async-to-generator',
'#babel/plugin-proposal-export-default-from',
'babel-plugin-jsx-control-statements',
'react-hot-loader/babel',
'lodash',
]
}
}
]
},
externals: [webpackNodeExternals()]
}
Is there a way I can tell webpack to transpile the jsx in the node_modules folder "on the go"? What other solutions could there be?
Also, the whole point of this project is to impprove the SEO for these apps, I would only need to SSR the index page and any direct link to content in it. No need to SSR the entire app is there a way this can be achieved?
I was also wondering if refactoring the entire app to use Next.js would be worth it if this were possible only for the index page and any direct link.
Thank you in advance!
If the team can afford it then you should definitely go and try a framework. This will me more maintainable on the long term. I would recommend you try Next.js over Gatsby, Both are great options but in my opinion Next has two or three advantages like Incremental Static Regeneration (Regenerate if your content change continuously) or you can choose between use Server Sider or Static generation based on your routes. You can use SSR on your dashboard and SSG on your home and landing pages for example.
If you don't need any type of pre-rendering you can go for client-side only and even then Next are gonna make a couple of optimization that will speed up your site.
On the long term it will save you a lot of time and it will be easy to maintain

React-hot-loader: `hot` could not find the `name` of the the `module` you have provided

I'm using Webpack 4 to create a React project with hooks and I'm trying to get the changes to reload on page live using react-hot-loader following this tutorial.
But I when I try npm start I get following error on the browser:
Error: React-hot-loader: hot could not find the name of the the
module you have provided
This is my App.js contents:
import React from 'react';
import { hot } from 'react-hot-loader';
import Header from './Header';
function App() {
return (
<section className="main">
<Header />
</section>
);
}
export default hot(App);
Alternately I tried importing hot from react-hot-loader/root, but this way I get a different error:
Error: React-Hot-Loader: react-hot-loader/root is not supported on
your system. Please use import {hot} from "react-hot-loader" instead
How could I solve this issue?
You should be requiring it before react:
import { hot } from 'react-hot-loader/root';
import React from 'react';
The package documentation mentions this.
Well, looking at my webpack configs:
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV) }),
],
devServer: {
contentBase: './dist',
hot: true,
},
I had used webpack.HotModuleReplacementPlugin() in plugins and hot: true in devServer which made the second error if I would use react-hot-loader/root.
So removing new webpack.HotModuleReplacementPlugin() from the webpack.config.js, solved my problem.
import { hot } from 'react-hot-loader';
export default hot(module)(App);
or
import { hot } from 'react-hot-loader/root';
export default hot(App);

Loading react component from url

I want to import the react component that I have bundled using web pack.
I am able to complete the task by copying it locally to that folder and then importing it like
import Any from '.dist/index'
and it is working fine.
But what I want to do is uploading this index.js file to somewhere for example Amazon s3. Now I am not able to import the component in the same way as mentioned above.
My webpack.config.js file, I have used to export my bundled component generated by webpack that I am using in another project by copying the index.js and index.css file is
var path = require("path");
var HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "index_bundle.js",
libraryTarget: "commonjs2"
},
module: {
rules: [
{ test: /\.(js)$/, use: "babel-loader" },
{ test: /\.css$/, use: ["style-loader", "css-loader"] }
]
},
externals: {
react: "commonjs react"
},
mode: "production",
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
})
]
};
I want to import the component from file url uploaded to s3.
you can do what you are describing with micro apps. A micro app is basically nothing more than a component that is lazy loaded into the host application from a url at runtime. There is no need to install or import the component at design time. There is a library available that lets you do this with a HOC component.
import React from 'react';
import ReactDOM from 'react-dom';
import MicroApp from '#schalltech/honeycomb-react-microapp';
const App = () => {
return (
<MicroApp
config={{
View: {
Name: 'redbox-demo',
Scope: 'beekeeper',
Version: 'latest'
}
}}
/>
);
});
export default App;
You can find more information on how it works here.
https://github.com/Schalltech/honeycomb-marketplace
This is not the way you should package and deploy your React components. AWS S3 is a bucket for storage of files to serve on the web. It's purpose is not to share code files through projects.
You should publish your React components to a registry such as NPM. After you publish your package to the registry, you should be able to install the package into your app as a dependency by doing something like npm install my_package.

React-hot-loader doesn't propagate changes

My problem is that react-hot-reload detect the changes and the modules that need to be update, and apparently does update the modules, but nothing in my browser changes.
Here's the version of the libraries I'm using:
react - 16.8.3
react-dom (patched by react-hot-loader ) - 16.8.3
react-hot-loader - 4.8.0
webpack - 4.29.6
Below, my webpack-dev-server config:
historyApiFallback: true,
hot: true,
stats: 'none',
quiet: true,
port: port,
host: host,
Relevant part of my webpack config
entry: [
'react-hot-loader/patch',
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/only-dev-server',
'./src/index.js',
],
resolve : {
alias: {
'react-dom': '#hot-loader/react-dom'
}
},
My .babelrc:
"plugins": [
"#babel/plugin-transform-flow-strip-types",
"#babel/plugin-proposal-class-properties",
"react-hot-loader/babel"
]
My initial component:
import React, { Component } from 'react'
import { hot } from 'react-hot-loader/root'
class App extends Component<PropTypes, StateTypes> {
render (): React$Element<*> {
return (
<Router>
<Switch>
<Route
path="/home"
render={ (props: Object): React$Element<*> => <HomeView {...props } /> }
/>
<Route component={ IntroView } />
</Switch>
</Router>
)
}
}
export default hot(App)
Lastly, my entry file:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './app'
const appContainer = document.getElementById('appContainer')
if (module.hot) {
module.hot.accept()
if (appContainer) ReactDOM.render(<App />, appContainer)
} else if (appContainer) {
ReactDOM.render(<App />, appContainer)
}
If I do any code change that was supposed to trigger an update, that's my console:
[WDS] App updated. Recompiling...
[WDS] App hot update...
[HMR] Checking for updates on the server...
XHR finished loading: GET "http://localhost:8080/df32ff4...hot-update.json.
[HMR] Updated modules:
[HMR] - ./src/library/protons/ModalWorkflow/sandbox/workflowStepOne.js
[HMR] - ./src/library/protons/ModalWorkflow/sandbox/index.js
[HMR] - ./src/configs/routes.js
[HRM] - ./src/views/intro/index.js
[HRM] - ./src/view/home/index.js
[HRM] - ./src/app.js
[HRM] App is up to date.
Browser doesn't reflect the code changes at all.
No errors, no warnings.
Tried several different approaches to check any potential mistake, but as of right now I'm in this hopeless spot, no idea what else I could try.

Resources