Is there any way to extend react-router of one application which is already hosted on fly? I want to inject additional routes on the click of a link which allows me to inject the script or allows to include my javascript.
Eventually I am looking for two different react applications which has one build and deployment cycle, but interrelated to each other.
Ex. there is the abc.com in which on click of a link(i.e. abc.com/nepage) the entire page is getting reloaded with same content [i.e. say header footer] which is maintained by different team all to gather and they have there one build and deployment cycle.
I want the application to be with SPA even if we have different build and deployment process.
This was achieved using Backbone with help of Backbone.Router.extend, where on click of link the default router for the new page was overridden with all new set of routers and which use to full the supporting files from the path mentioned for the specific router's
With PlainRoutes, child routes can be loaded on-demand (when the user enters the route) and resolved asynchronously. Having that in mind, you can use Webpack chunks to split the code corresponding to theses routes in diferente files. Going even further, you can have multiple entrypoints on Webpack, making users load only the part of the application that affects the current page.
Sample app:
index.js:
import React from 'react'
import ReactDOM from 'react-dom'
import { Router, browserHistory } from 'react-router'
const App = ({ children }) => {
<div>
<nav>Your navigation header</nav>
{ children }
<footer>Your app footer</footer>
</div>
}
const HomePage = () => <p>Welcome!</p>
const routes = {
path: '/',
component: App,
indexRoute: { component: HomePage },
getChildRoutes (partialNextState, cb) {
require.ensure([], (require) => {
cb(null, [
require('./routes/about'),
require('./routes/blog'),
require('./routes/contact'),
])
})
}
}
ReactDOM.render(
<Router history={ browserHistory } routes={ routes } />,
document.getElementById('container')
)
routes/about.js:
import React from 'react'
const About = () => <p>About page</p>
export default {
path: 'about',
component: About
}
Other routes could be similar to the about route as shown above.
Related
I have created a single page web app using react js. I have used webpack to create bundle of all components. But now I want to create many other pages. Most of pages are API call related. i.e. in the index.html, I have displayed content from API. I want to insert content in another page parsing data from API. Webpack compresses everything of react in a file which is bundle.js. However, the configuration of webpack is as follow:
const webpack = require('webpack');
var config = {
entry: './main.js',
output: {
path:'./',
filename: 'dist/bundle.js',
},
devServer: {
inline: true,
port: 3000
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015', 'react']
}
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
]
}
module.exports = config;
Now, I am confused what kind of configuration of webpack will be for other page or what is the correct way to build multi-pages app using react.js
Preface
This answer uses the dynamic routing approach embraced in react-router v4+. Other answers may reference the previously-used "static routing" approach that has been abandoned by react-router.
Solution
react-router is a great solution. You create your pages as Components and the router swaps out the pages according to the current URL. In other words, it replaces your original page with your new page dynamically instead of asking the server for a new page.
For web apps I recommend you read these two things first:
Full Tutorial
The react-router docs; it will help you get a better understanding of how Router works.
Summary of the general approach:
1 - Add react-router-dom to your project:
Yarn
yarn add react-router-dom
or NPM
npm install react-router-dom
2 - Update your index.js file to something like:
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render((
<BrowserRouter>
<App /> {/* The various pages will be displayed by the `Main` component. */}
</BrowserRouter>
), document.getElementById('root')
);
3 - Create a Main component that will show your pages according to the current URL:
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from '../pages/Home';
import Signup from '../pages/Signup';
const Main = () => {
return (
<Switch> {/* The Switch decides which component to show based on the current URL.*/}
<Route exact path='/' component={Home}></Route>
<Route exact path='/signup' component={Signup}></Route>
</Switch>
);
}
export default Main;
4 - Add the Main component inside of the App.js file:
function App() {
return (
<div className="App">
<Navbar />
<Main />
</div>
);
}
5 - Add Links to your pages.
(You must use Link from react-router-dom instead of just a plain old <a> in order for the router to work properly.)
import { Link } from "react-router-dom";
...
<Link to="/signup">
<button variant="outlined">
Sign up
</button>
</Link>
(Make sure to install react-router using npm!)
To use react-router, you do the following:
Create a file with routes defined using Route, IndexRoute components
Inject the Router (with 'r'!) component as the top-level component for your app, passing the routes defined in the routes file and a type of history (hashHistory, browserHistory)
Add {this.props.children} to make sure new pages will be rendered there
Use the Link component to change pages
Step 1
routes.js
import React from 'react';
import { Route, IndexRoute } from 'react-router';
/**
* Import all page components here
*/
import App from './components/App';
import MainPage from './components/MainPage';
import SomePage from './components/SomePage';
import SomeOtherPage from './components/SomeOtherPage';
/**
* All routes go here.
* Don't forget to import the components above after adding new route.
*/
export default (
<Route path="/" component={App}>
<IndexRoute component={MainPage} />
<Route path="/some/where" component={SomePage} />
<Route path="/some/otherpage" component={SomeOtherPage} />
</Route>
);
Step 2 entry point (where you do your DOM injection)
// You can choose your kind of history here (e.g. browserHistory)
import { Router, hashHistory as history } from 'react-router';
// Your routes.js file
import routes from './routes';
ReactDOM.render(
<Router routes={routes} history={history} />,
document.getElementById('your-app')
);
Step 3 The App component (props.children)
In the render for your App component, add {this.props.children}:
render() {
return (
<div>
<header>
This is my website!
</header>
<main>
{this.props.children}
</main>
<footer>
Your copyright message
</footer>
</div>
);
}
Step 4 Use Link for navigation
Anywhere in your component render function's return JSX value, use the Link component:
import { Link } from 'react-router';
(...)
<Link to="/some/where">Click me</Link>
This is a broad question and there are multiple ways you can achieve this. In my experience, I've seen a lot of single page applications having an entry point file such as index.js. This file would be responsible for 'bootstrapping' the application and will be your entry point for webpack.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Application from './components/Application';
const root = document.getElementById('someElementIdHere');
ReactDOM.render(
<Application />,
root,
);
Your <Application /> component would contain the next pieces of your app. You've stated you want different pages and that leads me to believe you're using some sort of routing. That could be included into this component along with any libraries that need to be invoked on application start. react-router, redux, redux-saga, react-devtools come to mind. This way, you'll only need to add a single entry point into your webpack configuration and everything will trickle down in a sense.
When you've setup a router, you'll have options to set a component to a specific matched route. If you had a URL of /about, you should create the route in whatever routing package you're using and create a component of About.js with whatever information you need.
Following on from the answer above by #lucas-andrade: for react-router versions >= 6, Switch no longer exists (see https://github.com/remix-run/react-router/issues/8439). The content of step 3 becomes:
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import Home from '../pages/Home';
import Signup from '../pages/Signup';
const Main = () => {
return (
<Routes>
<Route path='/' element={Home}></Route>
<Route path='/signup' element={Signup}></Route>
</Routes>
);
}
export default Main;
The second part of your question is answered well. Here is the answer for the first part: How to output multiple files with webpack:
{
entry: {
outputone: './source/fileone.jsx',
outputtwo: './source/filetwo.jsx'
},
output: {
path: path.resolve(__dirname, './wwwroot/js/dist'),
filename: '[name].js'
},
}
This will generate 2 files: outputone.js and outputtwo.js in the target folder.
One can also use gatsby a react framework dedicated to static multipage apps.
I am using Next.JS application routing system.
I have created a dynamic route with structure like pages/[country]/[language]/index.js.
Also there is a static route with structure pages/cz/cz/index.js.
Issue appears then i am on static page and trying to navigate throught Link component to access static route content in my case should go to home page & reload same page, instead of that dynamic route content is rendered.
In my case link is just simple navigation to home page <Link to="/"> for both routes.
But maybe issue lies on how index.js file is setup for predefined & dynamic routes.
cz/cz/index.js
export { default } from '../../../components/HomePage/';
const { getStaticProps } = createRoute({
path: '/',
locale: 'cs',
});
export { getStaticProps };
[country]/[language]/index.js
export { default } from '../../../components/HomePage/v2';
const { getStaticPaths, getStaticProps } = createRoute({
path: '/',
exclude: ['cs'],
otherLocales: ['cs'],
});
export { getStaticPaths, getStaticProps };
createRoute
export default function createRoute({
path,
otherLocales,
getPathParams,
locale,
} = {}) {
const locales = _.without(include, ...exclude);
return {
getStaticPaths: createGetStaticPaths({
locales,
getPathParams,
}),
getStaticProps: createGetStaticProps({
path,
locale,
locales,
otherLocales,
}),
};
}
Pages structure
So why [country]/[language]/index.js overrides cz/cz/index.js ?
So is there anything available in nextJS route to match a URL absolutely?
Or insure that going from static route should go to static route?
Following next.js documentation predefined routes take precedence over dynamic routes, and dynamic routes over catch all routes.
Take a look at the following examples:
pages/post/create.js - Will match /post/create
pages/post/[pid].js - Will match /post/1, /post/abc, etc. But not /post/create
pages/post/[...slug].js - Will match /post/1/2, /post/a/b/c, etc. But not /post/create, /post/abc
In your case you have defined predefined routes cz/cz/index.js and this route have priority
If you happen to be using redirects, note that they are processed before next ever goes to the pages.
Redirects are checked before the filesystem which includes pages and /public files.
https://nextjs.org/docs/api-reference/next.config.js/redirects
I have built authorization into my React App using passport.js, and I would like to, in my App.js file, fetch my authorization routes to see if a user is logged into the app, or if nobody is logged in.
To help with the question, I have shared a condensed version of my React App's App.js file, and Index.js file.
// App.js File
// Import React Libraries, Routes, Container Pages
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Route } from 'react-router-dom';
import { userActions } from './actions/auth/auth-actions.js';
import GameLanding from './containers/StatsPages/Game/GameLanding';
import AppFooter from './components/AppFooter';
// And Create The App
class App extends Component {
constructor(props, context) {
super(props, context);
}
componentDidMount() {
this.props.dispatch(userActions.authorize());
}
render() {
return (
<div>
<Route exact path='/stats/games' render={() => <GameLanding userInfo={this.props.userInfo} />} />
<AppFooter />
</div>
);
}
}
// export default App;
function mapStateToProps(reduxState) {
return {
userInfo: reduxState.authorizedReducer.userInfo,
authorized: reduxState.authorizedReducer.authorized,
loading: reduxState.authorizedReducer.loading
};
}
export default connect(mapStateToProps)(App);
... my entire App.js file has ~15 Routes components, and (part of) my goal with my App.js file is to fetch the authorized and userInfo props, and pass these to the components in the various routes. I showed an example where I pass the userInfo prop to the GameLanding component.
Here is how I have set up my Index.js file.
// Index.js
// Import Libraries
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
// Import CSS and the App
import App from './App';
import 'react-table/react-table.css';
import './index.css';
import './App.css';
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root'));
My current problem is as such: For some reason, fetching the userInfo and authorized props is breaking my app. I am unfortunately getting no error messages... rather, all of the react-router-dom Links in my app are simply not working... clicking them changes the url, but the pages of my app no longer change...
My questions are then, (a) am i allowed to fetch authorization data in App.js in the manner I am doing so (using connect, with mapStateToProps, etc.), or am I doing this all wrong?
Whether or not somebody is logged into my app is an app-wide thing, not a page-specific thing, and I figured for this reason (also to prevent having to fetch auth props in many many container pages) that App.js is the best place to grab these props.
Any thoughts on why my app is breaking, or how else my App.js file should look (I am ~99% sure my index.js is fine), would be greatly appreciated! Thanks!
Edit: For reference, doing the following: (i) importing userActions, (ii) calling userActions.authorize() in componentDidMount, (iii) including the mapStateToProps and connect on bottom of app, etc. works for loading the auth props in any of my container components. e.g. if i had this code in my GameLanding component, it doesnt break the react-router-dom Links app-wide in the same manner that it does when this code is in App.js. Hence the title of the question. Thanks!
1) Reason for app breaking:
I am assuming userInfo and authorized props will be undefined, as component renders initially before componentDidMount runs and you have not handled undefined props. You could also pass default props for these props.
2) Better structure for authorization
I am assuming you need to authenticate each route for authorization.
i) Create routes file and enter all routes for your app.
ii) <Route exact path='/stats/games' component={GameLanding} onEnter={reqAuth}/>
Inside reqAuth function you should check if the user is authorized for that route or not.
iii) Inside App component call action for fetching data, store in store and use GameLanding as child component and pass props only when they are defined.
That is not whole code, but should give you gist.
Happy Coding!!!
I want to introduce lazy loading to Vue Router, so that some parts of the code will be loaded only on demand.
I'm following the official documentation for Lazy Loading in Vue Router:
https://router.vuejs.org/en/advanced/lazy-loading.html
So for a test I've changed how the Vault module is imported in my router file:
import Vue from 'vue';
import Router from 'vue-router';
// Containers
import Full from '#/containers/Full';
// Views
// TODO: views should be imported dynamically
import Dashboard from '#/views/Dashboard';
const Vault = () => import('#/views/Vault');
import Page404 from '#/views/Page404';
import Page500 from '#/views/Page500';
import Login from '#/views/Login';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
redirect: '/dashboard',
name: 'VENE',
component: Full,
children: [
{
path: 'dashboard',
name: 'dashboard',
component: Dashboard
},
{
path: 'vault',
name: 'vault',
component: Vault
},
],
},
{
path: '/login',
name: 'Login',
component: Login,
},
{
path: '/404',
name: 'Page404',
component: Page404,
},
{
path: '/500',
name: 'Page500',
component: Page500,
},
],
});
All fine, however, when I open the app for the first time, the extracted bundle which was supposed to be lazy loaded, is loaded up front:
When I go to that view using router it appears in Dev Tools Network Tab again, but says it's loaded from the disk, so the bundle is clearly loaded on first page load, which is totally against the idea of lazy loading.
This is occurring for a couple reasons. I should say, you've set everything up correctly for lazy-loading the Vault component. One tip, I've found it helpful to add the webpack chunk name to the dynamic import:
const Vault = () => import(/* webpackChunkName: "vault" */ '#/views/Vault')
This would then show up in your network tab named with the chunkName "vault"
First, I'm guessing that you're using #vue-cli looking at your file structure and /src alias. Depending on the options you select when creating your project, #vue-cli uses a webpack config for progressive web apps that prefetches all resources. While the browser has mechanisms for prioritizing these downloads, I've found that some of the prefetching appears to block other resources. The benefit of prefetching is for browsers that don't support service-workers, you use idle browser time to put resources in the browser cache that the user will probably eventually use. When the user does need that resource, it is already cached and ready to go.
Second, you do have options for disabling the prefetch plugin. #vue-cli provides escape hatches for overriding the default config. Simply edit or add vue.config.js to the root of your project.
courtesy #LinusBorg
// vue.config.js
chainWebpack: (config) => {
// A, remove the plugin
config.plugins.delete('prefetch')
// or:
// B. Alter settings:
config.plugin('prefetch').tap(options => {
options.fileBlackList.push([/myasyncRoute(.)+?\.js$/])
return options
})
}
-- Be sure to only use either option A or option B; not both. --
Source: https://github.com/vuejs/vue-cli/issues/979
I've used option A with success, but you should definitely benchmark the results yourself and go with the option that best serves your users and application.
I appreciate the configurability of #vue-cli for these and many scenarios. It's definitely worth exploring to write the application you want, rather than coercing your app to the config.
I am using React Router in my current project:
const store = Redux.createStore(bomlerApp);
const App = React.createClass({
render() {
return (
React.createElement('div', null,
this.props.children
)
)
}
})
var rootElement =
React.createElement(ReactRedux.Provider, {store: store},
React.createElement(ReactRouter.Router, {history: ReactRouter.browserHistory},
React.createElement(ReactRouter.Route, { path: '/', component: App },
React.createElement(ReactRouter.IndexRoute, { component: Home })
)
)
)
ReactDOM.render(rootElement, document.getElementById('react-app'));
This does not work. The app does not render at all and I don't get any error messages.
However, if I use ReactRouter.hashHistory instead, the app works.
What am I not understanding here?
Server Configuration: the browser history setup can generate real
looking urLs without reloading the page. But what happens if the user
refreshes or bookmarks on a deep nested urL? these urLs are
dynamically generated at the browser; they do not correspond to real
paths on the server, and since any urL will always hit the server on
the first request, it will likely return a page not Found error.
To implement the browser history setup, you need to import the
createBrowserHistory method from the History library. You can then
invoke it, passing the generated browser history configuration as the
history prop of the Router component
***> to work with browser history setup, you need to make rewrite
configurations on your server, so when the user hits /some-path on the
browser, the server will serve index page from where react router will
render the right view.***