How can I modify React build to give me multiple JavaScript files? - javascript

I have a multi-page application that doesn't use Node. It has a bunch of HTML and JavaScript files. I want to render React components in the application, which I found I am able to do with ReactDOM.render.
However, I am building out the React components separately in a project made with npx create-react-app. I can use npm run build which gives me a main.js file that contains a production ready version of all my components.
I can add this main.js file into my HTML files with a script tag to have my components render in my multi-page application.
However, the problem is that I notice when I run npm build, I get one main.js file that contains everything. This is not good because the app will run too slow if I have to load all of my React components in main.js file every time I go to a new page.
Instead, I am looking to only load the specific components I need for a page.
Example:
Let's say I had a test1 component and a test2 component.
Page1 has a script to use test1, so I only want to load test1 on Page1.
Page2 has a script to use test2, so I only want to load test2 on Page2.
Therefore, I need a test1.js file and a test2.js file after npm run build
How would I do this?

Sounds like a use case for Code Splitting.
If you want to split per-page with React Router you can do the following:
import React from "react";
import { render } from "react-dom";
import {
BrowserRouter,
Routes,
Route,
} from "react-router-dom";
const Home = React.lazy(() => import("./Home"));
const Contact = React.lazy(() => import("./Contact"));
render(
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</BrowserRouter>,
document.getElementById("root")
);
This should generate multiple JS files for different components (e.g. pages) when used with some standard build tools like create-react-app.

Related

How is it possible to access homepage from package.json in a react app?

I created a react app that will be served from /example-path. I defined it in the package.json like this:
"homepage":"/example-path"
It has worked so far, but now I would like to add routing with react-router-dom, and it incorrectly detects /example-path as the part of the URL.
This is my code:
<Router>
<Switch>
<Route path="/product/:id" children={<DisplayProduct />} />
</Switch>
</Router>
Unfortunately react-router-dom tries to match the full URL /example-path/product/10. How is it possible to avoid that? Is it possible to access the homepage variable somehow?
I could use <Router basename="/example-path">, but in that way, this parameter is a duplicate. I would like to define it in only one place.
How is it possible to access homepage from package.json in a react app?
We can access the URL set in package.json's homepage field using PUBLIC_URL environment variable.
We sometimes use a relative URL to deploy the application in subdirectories for example we are deploying the application in https://myhostname.com/example-path.
In order to do that, we can either set the environment variable PUBLIC_URL=/example-path for the application build or set the package.json homepage attribute to "/example-path". In your case, you have set the
"homepage": '/example-path/'
then the PUBLIC_URL would be set to /example-path/. Now you can access this environment variable anywhere in your react application.
<Router basename={process.env.PUBLIC_URL}>
<Switch>
<Route path="/product/:id" children={<DisplayProduct />} />
</Switch>
</Router>
After running the npm run build, you can check in the build/index.html that all the places where you used the %PUBLIC_URL% is set to /example-path/ like below;
<script type="text/javascript" src="/example-path/static/js/main.ec7f1972.js">
The Router doesn't know anything about your package.json. It just looks at the browser location. You can set a basename if you use BrowserRouter:
import {BrowserRouter as Router} from 'react-router';
<Router basename="/example-path">
{/* ... */}
</Router>
Edit: With the addition of environment variables to react-scripts#0.2.3 and higher prefer #Alan Omars answer: Prefix an environment variable with REACT_APP_ and it will be exposed to the client at build time. docs
Here's the old answer that uses the fact that Webpack blindly replaces %PUBLIC_URL%in index.html:
Assuming you are using create-react-app, this is actually pretty straightforward:
Add the following to public/index.html:
<script>
window.PUBLIC_URL = "%PUBLIC_URL%";
</script>
now you can reference the homepage from JavaScript! Simply access it via
const myPublicUrl = window.PUBLIC_URL;
Note: The variable will be an empty string if no homePage field is set in the package.json, so we have to compensate for that:
<Router basename={window.PUBLIC_URL && window.PUBLIC_URL.length > 0 ? window.PUBLIC_URL : "/"}>
{/* ... */}
</Router>
Since you are using create-react-app package you can use environment variable instead of "homepage":"/example-path" in your package.json:
create .env file in the root of your project.
add REACT_APP_HOME_PAGE='/example-path/' to the .env file
now you can access your env variable through
process.env.REACT_APP_HOME_PAGE in your app like the following:
<Router basename={process.env.REACT_APP_HOME_PAGE}>
I don't know where is the location of your Router file but you can try to import it as json in src/index.js file that created by create-react-app cli app like below;
import myPackageJson from '../package.json';
or
import { homepage } from '../package.json';

Use multiple components on one React Page

I am new to learning React and so far as I have been exploring its amazing. I have a quick question which is how can I display multiple modules such as "app.js" and "About.js" on one page? is this possible?
Here is some images that may help.
React App.js and About.js
React index.js
React js console error
Commonly in a react app, you will route based on different URLs.
The different 'modules' (also known as components) are typically defined in other JS files. See the HomePage and AboutPage files in the example below.
First, import the other pages.
Second, route to them given a path.
A common routing solution in React is React Router
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import HomePage from './pages/HomePage.js';
import AboutPage from './pages/AboutPage.js';
const MyApp = () => (
<BrowserRouter>
<Switch>
<Route path="/home" component={HomePage} />
<Route path="/about" component={AboutPage} />
</Switch>
</BrowserRouter>
);

404 Error when deploying React App on custom domain

It seems like I followed all the necessary in the documentation but it keeps giving a 404 Error: "There isn't a GitHub pages here" when I go into https://juliesong.me.
I got my custom domain through GoDaddy and configured the DNS like so:
Then I changed my package.json file to have
"homepage": "https://www.juliesong.me",
Added a CNAME file in the root directory containing
www.juliesong.me
And lastly went into settings in GitHub pages:
I searched my issue up and a lot of people said it might have to do with the React Router, so I edited to include a basename:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { HashRouter as Router } from "react-router-dom";
import * as serviceWorker from './serviceWorker';
const routing = (
<Router basename={process.env.PUBLIC_URL}>
<App />
</Router>
);
ReactDOM.render(routing, document.getElementById('root'));
This is my App.js:
import React, { Component } from 'react';
import {Route} from 'react-router-dom';
import { withRouter } from 'react-router'
import { GlobalStyles, } from "./util/GlobalStyles";
import Home from './containers/Home'
class App extends Component {
render() {
const home = () => <Home />
return (
<main>
<GlobalStyles />
<React.Fragment>
<Route exact path="/" component={home} />
</React.Fragment>
</main>
);
}
}
export default withRouter(App);
If anyone could help on this issue, that would be great:)
Try this one
add new file named .htaccess in your build folder
example of file
and add this code into your .htaccess file
the code
Unless you share how you are generating the build and pushing it to GitHub Pages, it is very difficult to identify the cause of this issue.
Still let me share a general way you would deploy a react app in GitHub pages.
You don't need to add basename in your router
Good thing is you are using HashRouter instead of BrowserRouter, because GitHub pages doesn't support history api like normal browser, it will look for a specific file in given path
To deploy your app you need to run the following command in same order -
npm run build # this will generate a build folder
gh-pages -d build # this will publish the build folder to GitHub pages
This is very important that you publish the build folder, because
its contains the index.html file, which GitHub will point.
You can read more about deploying react app to GitHub Pages here
React-app out of the box is client-server application, and you should compile it first to html + client side js before deploying.
There are lots of good docs: create-react-app.dev, medium.com and the shortest way here

Why react-router does not display correct component?

When I enter localhost:8080 in my browser it displays App.js component. But when I navigate to localhost:8080/#/hello it displays same App.js component instead of hello.js. localhost:8080/hello show "can not get localhost:8080/hello" . What is the problem with my code? I am using webpack and babel in my App.
//index.js
import React from 'react';
import ReactDOM, { render } from 'react-dom';
import { Provider } from 'react-redux';
import {store} from './public/store/store';
import App from './public/Components/App';
import Hello from './public/Components/hello';
import {
BrowserRouter as Router,
Route
} from 'react-router-dom';
//import './index.css'
render(
<Provider store={store}>
<Router>
<div>
<Route path="/" component={App}/>
<Route path="/hello" component={Hello}/>
</div>
</Router>
</Provider>,
document.getElementById('root')
)
//App.js
import React from 'react';
export default class App extends React.Component {
render() {
return (
<div>
<h1>React Js.</h1>
</div>
);
}
}
//hello.js
import React from 'react';
export default class Hello extends React.Component {
render() {
return (
<div>
<h1>Hello</h1>
</div>
);
}
}
There are a few things happening here, let me try to explain what's going wrong and how you can fix them.
http://localhost:8080/#/hello it displays the same App.js component instead of hello.js.
Because you are using BrowserRouter instead of HashRouter (an older version, # is not working). The browser reads only the first part of your URL which is http://localhost:8080/. The # is similar when you route to a section of a page using the following.
Goto projects
The above keeps the user on the same page but scrolls to the section <div id="projects"></div>
Don't use this, if your are using React Router V4 it's not what you are looking for.
http://localhost:8080/hello displays cannot get http://localhost:8080/hello
You probably don't have a dev server running that supports front-end routing. If you don't, basically what is happening is that by pressing enter you tell the SERVER to serve you page, http://localhost:8080/hello. You don't want this, the server should be passive here and not serve you any other page then your main index.html. So, instead, you want the server to give you http://localhost:8080 and by doing so, it loads your main index.html and scripts, then react takes over, react-router checks the url and it then renders you the /hello route with the Hello component.
In order to achieve this make sure you have webpack-dev-server installed. You can do this by typing the following in the command line.
npm install webpack-dev-server --save-dev
Then add the following to your package.json
devServer: {
publicPath: '/',
historyApiFallback: true
}
// add the below line to the scripts section
"start:dev": "webpack-dev-server"
This basically tells the dev-server to re-route all requests to index.html, so react-router takes care of the routing. Here's more on Webpack-dev-server.
Then in your terminal run npm run start:dev to start the development server.
I hope this all makes sense and with these guidelines you're able to make your code work. If not let me know ;)
NOTE: Alex has a good point as well. React Router v4 renders all routes that match. So, if the path is http://localhost:8080/hello
/ and /hallo both match and will render. If you only want to render one, use exact as Alex mentions, or wrap your routes in a <Switch> component.
<Switch>
<Route path="/" component={App}/>
<Route path="/hello" component={Hello}/>
</Switch>
Here's what the react-router docs say.
Renders the first child or that matches the location
UPDATE:
After the OP uploaded a repo with the problem, the following was corrected to make the routes work. If anyone is interested, the fixed project is on GitHub.
Main points to make the project work:
Use react-router-dom instead of react-router
Tell Express route all incoming traffic to index.html app.get("/*", (req, res) => res.sendFile(__dirname + '/public/index.html'));
Use <Switch> and <Route> components to setup the routes as described in the question. See code here.
Try to use exact directive:
<Route exact path="/" component={App} />
See API doc: https://reacttraining.com/react-router/web/api/Route/exact-bool

react-router-dom BrowserRouter not working after build

This is the first time I tried react.
I create a project using create-react-app, then for routing, I use React Router V4 for web react-router-dom.
This is my index.js file
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import App from './App';
import './index.css';
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root')
);
There's no problem when I start the server with npm start. But when I build the app with npm run build, putting it in my www folder in my Apache server and access it, I only get empty page with <!-- react-empty: 1 --> inside my root div.
<div id="root">
<!-- react-empty: 1 -->
</div>
I have tried looking for similar cases, but the answer always about adding historyApiFallback in webpack.config.js or gulp, which I can't find anywhere in the create-react-app template.
I have tried HashRouter, it's working fine, but there's /#/ in the url which is undesirable.
How can I get the BrowserRouter to work after I build the app?
Well, there was no problem at all. It worked, even if it was built using npm run build. My best guess is: you placed the files in the wrong folder.
When you build your code using npm run build, the static folder should be placed inside the MallApp folder. So your folder structure should be like this:
/var/www/mywebfolder/index.html
/var/www/mywebfolder/MallApp/static
Check your browser's console, I believe it contains errors. Failed to get the js and css files.

Categories

Resources