I am trying to develop an app that takes other "apps" as "plugins". This base app would include only basic auth routes and other apps would define their own routes within them.
How can I accomplish this with React? I suppose React Router could have something, but I have not been able to find it.
I come from a Ruby on Rails world where I could have a gem as an engine and on the base app I would just mount the engine on a given path. I was looking for something similar to that, such that on my base App.js I could simply import ModuleARoutes from 'module-a' and somehow insert it into the base app's <Router> component like:
<Router>
<ModuleARoutes path="/module_a" />
</Router>
Any guidance is much appreciated! Thanks!
UPDATE
Using the answer from #felipe-lanza I had my ModuleA like this:
import React from 'react';
import { Route } from 'react-router';
import { BrowserRouter } from 'react-router-dom';
const Example1 = () => (<div>test 1</div>);
const Example2 = () => (<div>test 2</div>);
const App = () => (
<BrowserRouter>
<div>
<Route exact path="/" component={Example1} />
<Route exact path="/example1" component={Example2} />
</div>
</BrowserRouter>
);
export default App;
export { App as ExampleApp };
And on my base app I have
import MainStore from './stores/MainStore';
import AuthStore from './stores/AuthStore';
import App from './App';
import ExampleApp from '#module-platform/example';
const stores = { MainStore, AuthStore };
const Routes = () => (
<Provider { ...stores }>
<Router>
<Switch>
<Route exact path="/" component={ Login } />
<Route path="/dashboard" component={ App } />
<PrivateRoute path="/example_app" component={ ExampleApp } />
<Route component={ NotFound } />
</Switch>
</Router>
</Provider>
);
And now if I navigate to localhost/example_app I do get the expected result (a div with "test 1"). However, I would expect that navigating to localhost/example_app/example_1 would render the div with "test 2", however it still renders "test 1". As a matter of fact, any location with localhost/example_app (e.g. localhost/example_app/asdfasdfa) will get me the "test 1" div rendered.
What am I doing wrong?
If I understood correctly, wouldn't that be akin to rendering the child apps below the base app as different routes?
I.e. (inside your index.js):
<Router>
<Route path='/' component={BaseApp}/>
<Switch>
<Route path='/child-path-1' component={ChildApp1}/>
<Route path='/child-path-2' component={ChildApp2}/>
<Route path='/child-path-n' component={ChildAppN}/>
</Switch>
</Router>
Then each child app could have its own routes, and so forth.
Related
Trying to deploy my first React application. The index route is visible but none of the relative endpoints work and I get a 404 from the render server. However I read that I needed to make these changes to the deployment in order for the client-side routing to work properly:
Render's suggestion for configuring client-side routing to work properly
However, when I visit an endpoint like "/login" or "/signup", the react router in my app catches it as a 404 and
renders the 404 page, and the endpoint in the url changes to /index.html
index.js
import React from "react";
import ReactDOM from "react-dom/client";
import Views from "./App";
import { BrowserRouter } from "react-router-dom";
import { UserProvider } from "./context/userContext.js";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<UserProvider>
<BrowserRouter>
<Views />
</BrowserRouter>
</UserProvider>
</React.StrictMode>
);
App.js
import { Routes, Route } from "react-router-dom";
import PublicRoutes from "./routes/PublicRoutes";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { useUser } from "./context/userContext.js";
function Views() {
const { loading } = useUser();
if (loading) return <h1>Loading...</h1>;
return (
<div className="App">
<Routes>
<Route path="*" element={<PublicRoutes />} />
</Routes>
<ToastContainer />
</div>
);
}
export default Views;
Routing Logic:
const PublicRoutes = () => {
return (
<QueryClientProvider client={new QueryClient()}>
<Routes>
<Route index element={<Landing />} />
<Route path="login" element={<Login />} />
<Route path="signup" element={<SignUpMUI />} />
<Route element={<Protect />}>
<Route path="dashboard" element={<Dashboard/>} />
<Route path="event/:id" element={<EventPage />} />
<Route path="profile" element={<>Profile Page</>} />
<Route path="settings" element={<>Settings</>} />
</Route>
<Route path="*" element={<h1>404</h1>} />
</Routes>
</QueryClientProvider>
);
};
export default PublicRoutes;
I thought maybe it has something to do with the file pathing because my repository contains a sub-folder for the API and then a sub-folder for the react application so maybe I thought I had to route the html pathing as /client/index.html or something but that didn't work. Honestly I have no idea what I am supposed to do, I have very little experience with deploying and with most resources and tutorials covering how to deploy with Heroku (which has recently deprecated their free tier) and most tutorials covering React deployment on Render don't involve any usage of the react router.
Also, I would like to reiterate the structure of the repository as it contains two sub folders, one which contains the react application called "client" and another which contains the server code called "server". Here is an image
I'm thinking maybe this has something to do with the redirections to the index.html but Idk, I've already tried messing about with the configuration on render to see if it would make a difference but no dice.
Just read: https://render.com/docs/deploy-create-react-app
and add this to Redirects/Rewrites section
enter image description here
like the title says I need to create separate component where I will store routes for my project. This is what I have done in App.js component:
<Router>
<Switch>
<Route path="/" exact />
<Route path="/messages/messagelist" exact />
<Route path="/messages/ceostats" exact />
<Route path="/resourcem/categories" exact />
<Route path="/resourcem/subcategories" exact />
<Route path="/resourcem/resources" exact />
<Route path="/surveys" exact />
<Route path="/userm/users" exact />
<Route path="/userm/usergroups" exact />
</Switch>
</Router>
What I need to do now, is to extract them for example in separate component let's call it Route.js. How do I do that?
I prefer to separate the RouterProvider in this way:
First, let create a RouterProvider.js:
import React from 'react';
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom';
function RouterProvider() {
return (
<Router>
<Switch>
// Routes
</Switch>
</Router>
)
}
export default RouterProvider;
Now, create a routes.js file:
import HomePage from 'pages/home';
import AboutUsPage from 'pages/aboutUs';
const routes = [
{
path: '/home',
component: HomePage,
exact: true
},
{
path: '/aboutUs',
component: AboutUsPage,
exact: true
},
// and so on for other routes in your project
]
export default routes;
Back to the RouterProvder.js and import the routes object and use it:
import React from 'react';
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom';
import routes from 'path/to/routes' // ---> import from routes.js file
function RouterProvider() {
return (
<Router>
<Switch>
{
routes.map(route =>
<Route path={route.path} component={route.component} exact={route.exact} />
)
}
</Switch>
</Router>
)
}
export default RouterProvider;
Finally, add the RouterProvider as the last layer in the App.js file:
import RouterProvider from 'path/to/providers/routerProvider'
function App() {
return (
<ReduxProvdier>
<RouterProvider/>
</ReduxProvider>
)
}
Note: ReduxProvider is just an example of other providers in your application like Toast provider or Network provider.
Note: creating routes object in routes.js is not useful in all cases for example if you wanna use Suspense/Lazy it's not working as your expectation.
<Route path="/" component = { ... } exact /> // read docs for more info
https://reactrouter.com/web/api/Route/component
I am developing a system with a login, and I want the drawer to appear only on routes that are private. How can I do that, below is a code that I already tried and was not successful.
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Home from "./Home";
import Login from "./Login";
import Details from "./Details";
import { AuthProvider } from "./Auth";
import PrivateRoute from "./PrivateRoute";
import Drawer from "./components/Drawer";
const App = () => {
return (
<AuthProvider>
<Router>
<Route exact path="/login" component={Login} />
</Router>
<Router>
<RoutePrivate />
</Router>
</AuthProvider>
);
};
const RoutePrivate = () => {
return (
<Router>
<Drawer />
<PrivateRoute exact path="/" component={Home} />
<PrivateRoute exact path="/:id" component={Details} />
</Router>
);
};
export default App;
You could do conditional rendering of drawer
const RoutePrivate = () => {
return (
<Router>
{window.location.pathname !== '/login' ? <Drawer /> : null}
<PrivateRoute exact path="/" component={Home} />
<PrivateRoute exact path="/:id" component={Details} />
</Router>
);
};
Good luck
You can go with few options.
you can remove <Drawer /> from router and render it inside <PrivateRoute /> along with matched router component (Home or Details etc).
Inside Drawer component, you can check auth and render null if auth failed. This way public routes will never get to see Drawer compoennt.
create a common layout and put the router inside layout. Check this. Also I got a random codesandbox (not fully working) but you can see the code reference there.
I am trying to navigate to /movie/:title on which I am rendering a new page. But it only renders if my movie page i.e /movie renders, but I don't want my movie page to be mounted on navigating to /movie/:title. I only want it as part of the path. Making the /movie path exact doesn't work as /movie/:title wouldn't render with /movie as exact.
App.js
class App extends React.Component {
render() {
return (
<div>
<Header />
<Route path='/movies' component={Movies} />
<Route path='/tvshows' component={TvShow} />
</div>
)}
Movie.js
return (
<div className="movies">
<Route path={`${match.path}`} render={(props) => <CollectionGrid movies/>} />
<Route path={`${match.path}`} render={(props) => <CollectionOverview movies/>} />
<Route path={`${match.path}/:title`} component={ItemPage} />
</div>
);
A couple of notes. Conventionally, you should keep all your routes in a single router, thus making it easier to navigate and identify your application.
Organize your Router like this:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./Home";
import Movies from "./Movies";
import ItemPage from "./ItemPage";
const App = () => {
return (
<BrowserRouter>
<div>
<Switch>
<Route path="/" component={Home} exact />
<Route path="/movies/:title" component={ItemPage} />
<Route path="/movies" component={Movies} />
</Switch>
</div>
</BrowserRouter>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Additionally, to resolve your solution, you would need the Switch component, which is available in react-router-dom. The Switch component, will look through a list of Routes and will only render the FIRST Route who's path string is included within the URL string.
In the list above we have 3 Routes. If your URL is "/movies/blah", it will only render the ItemPage component, because that Route's path was matched first within the list.
We moved the "/movies/:title" Route before the regular "/movies" for this very reason. If the "/movies" Route appeared first in the list and if your URL was "/movies/blah", it would still satisfy the "/movies" Route. That means Switch would only render Movies component, which is not what you want.
See working sandbox: https://codesandbox.io/s/hopeful-bogdan-lzl76
you are not able to access to match direct in this app the match is found in this .props.match
you cand used it in
const App({match})
then when you need to used it should
<Route path={`${match.path}`} render={(props) => <CollectionGrid movies/>} />
look to this example and explain
I'm starting in React and I'm curious about about if have any way to change a page without reload all the html, changing only a content component for example.
I know that there is a way to change the component without change the url but I thought that if the url change too the application would be better.
React Router is the exact thing you're looking for
Here, how you can achieve what you're looking for.
First, wrap your app with BrowserRouter
import { BrowserRouter } from "react-router-dom";
import React from 'react';
class App extends React.Component {
return (){
return (
<BrowserRouter>
<SomeComponent />
</BrowserRouter>
)
}
}
Now just use the Route and Link. Route told the application which component to render on the basis of the current route and Link changes the URL without reloading the whole page
import { Route, Link, Switch } from "react-router-dom";
import React from 'react';
import {Circle, Square} from './someFileWithComponents';
class SomeComponent extends React.Component {
render(){
return (
<div>
<Link to='/circle' >Circle</Link>
<Link to='/square' >Square</Link>
<Switch>
<Route path='/circle' component={Circle} />
<Route path='/square' component={Square} />
</Switch>
</div>
)
}
}
React Router is what you looking for
const AppRouter =()=>(
<BrowserRouter>
<div>
<Header/>//where Header components contains the navigation
<Switch>
<Route path="/" component={BookListPage} exact={true} />
<Route path="/create" component={AddBookItem} />
<Route path="/edit/:id" component={EditBookItem} />
<Route path="/help" component={HelpPage} />
<Route component={NotFoundPage} />
</Switch>
</div>
</BrowserRouter>
);
export default AppRouter;