I'm building a movie app that pulls data from themoviedb API. Right now, on the home page, I have a search form where I type the movie I want to look for. Then below, I display the result of the movies returned with a button that takes you to a page that displays more info about a particular movie.
My issue is with the routing. How do I do it in such a way that when I'm on the movie-details page, the search and results components are not displayed
import React, { Component } from "react";
import { Route, Switch } from "react-router-dom";
import Navbar from "./navbar";
import Search from "./search";
import Result from "./result";
import Details from "./details";
import "./App.css";
import axios from "axios";
class App extends Component {
state = {
searchTerm: "",
movies: []
};
onSearch = e => {
e.preventDefault();
this.setState({ searchTerm: e.target.value });
};
handleSubmit = async e => {
e.preventDefault();
const result = await axios.get(
`https://api.themoviedb.org/3/search/movie?query=${this.state.searchTerm}&page=1&api_key=6f7ad5c4744feea1ee5508d2e56232c4`
);
this.setState({movies: result.data.results})
console.log(result.data.results);
};
render() {
return (
<div className="container">
<Navbar />
<Switch>
{/* <Search handleSearch={this.onSearch} /> */}
{/* <Route exact path="/" component={Search} handleSearch={this.onSearch} handleSubmit={this.handleSubmit}/> */}
= <Route
exact
path="/"
render={props => (
<Search
{...props}
handleSearch={this.onSearch}
handleSubmit={this.handleSubmit}
/>
)}
/>
<Route path="/movie/" component={Details} />
<Route path="/" component={Result} />
</Switch>
<Result movies={this.state.movies}/>
</div>
);
}
}
export default App;
https://codesandbox.io/s/github/peoray/movies-info
Your <Result /> component is outside of the router, so it's always being displayed. A quick fix here is to move it inside the router, like so:
return (
<div className="container">
<Navbar />
<Switch>
{/* <Search handleSearch={this.onSearch} /> */}
{/* <Route exact path="/" component={Search} handleSearch={this.onSearch} handleSubmit={this.handleSubmit}/> */}
<Route
exact
path="/"
render={props => (
<>
<Search
{...props}
handleSearch={this.onSearch}
handleSubmit={this.handleSubmit}
/>
<Result movies={this.state.movies} />
</>
)}
/>
<Route path="/movie/" component={MovieDetails} />
</Switch>
</div>
);
https://codesandbox.io/s/todo-app-r38v5
Related
const Navbar = () => {
return (
<div>
{location === '/' ? (
<AuthNav />
) : location === '/home' && isAuthenticated ? (
<MainNav />
) : <AuthNav />
}
</div>
);
};
How do I render two separate navbars on different application routes, in this case, I want to render the AuthNav in the login and signup path and I want to render MainNav on the home path.
Issues
I think you've a few things working against you:
The Navbar component is unconditionally rendered and using window.location.pathname to compute which actual navigation component to render. This means the view to be rendered is only computed when the Navbar component rerenders.
The Navbar component is rendered outside the Routes, so it's not rerendered when a route changes.
Solution
Instead of unconditionally rendering Navbar and trying to compute which nav component to render based on any current URL pathname, split them out into discrete layout routes that render the appropriate nav component.
Example:
Navbar.jsx
export const AuthNav = ({ auth }) => {
....
};
export const MainNav = () => {
....
};
App.jsx
import { Routes, Route, Navigate, Outlet } from 'react-router-dom';
import { useState } from "react";
// components
import { AuthNav, MainNav } from './components/Navbar';
// pages
...
...
const AuthLayout = ({ auth }) => (
<>
<AuthNav auth={auth} />
<Outlet />
</>
);
const MainLayout = () => (
<>
<MainNav />
<Outlet />
</>
);
const PrivateRoute = ({ auth }) => {
return auth.isAuthenticated
? <Outlet />
: <Navigate to="/" replace />;
};
const App = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
return (
<div className='parent'>
<Routes>
<Route element={<AuthLayout auth={{ isAuthenticated, setIsAuthenticated }} />}>
<Route path='/' element={<SignIn />} />
<Route path='/signup' element={<SignUp />} />
</Route>
<Route element={<MainLayout />}>
<Route element={<PrivateRoute auth={{ isAuthenticated }} />}>
<Route path='/Home' element={<Home />} />
<Route path='/music' element={<Music />} />
<Route path='/genre/' element={<Pop />} />
<Route path='/Hiphop' element={<HipHop />} />
<Route path='/Rock' element={<Rock />} />
<Route path='/EDM' element={<EDM />} />
<Route path='/Jazz' element={<Jazz />} />
<Route path='/RandB' element={<RandB />} />
<Route path='/store' element={<Store />} />
<Route path='/News' element={<News />} />
<Route path='/Contact' element={<Contact />} />
<Route path='/album/:id' element={<Album />} />
<Route path ="/album/:id/nested/" element={<Albums2 />} />
</Route>
</Route>
</Routes>
</div>
);
};
This question already has answers here:
How to create a protected route with react-router-dom?
(5 answers)
Closed 4 months ago.
Help
I'm using a protected route in my React app. But it is not working. All the other Elements are working but when I got to "/account" the whole screen is white. This is my code. It will be really helpful for me if you give that answer. Thank You :)
Protected Route code:
import React, { Fragment } from 'react';
import { useSelector } from 'react-redux';
import { Route, Routes, redirect } from 'react-router-dom';
const ProtectedRoute = ({ element: Element, ...rest }) => {
const { loading, isAuthenticated, user } = useSelector(state => state.user);
return (
<Fragment>
{!loading &&
(<Routes>
<Route
{...rest}
render={(props) => {
if(!isAuthenticated) {
return redirect("/login")
}
return <Element {...props} />
}}
/>
</Routes>
)}
</Fragment>
)
}
export default ProtectedRoute;
I am using ProtectedRoute.js in App.js. Here is the code.
App.js Code:
import React from 'react';
import {BrowserRouter as Router,Route,Routes} from "react-router-dom";
import './App.css';
import Header from "./component/layout/Header/Header.js";
import webFont from "webfontloader";
import Footer from './component/layout/Footer/Footer';
import Home from "./component/Home/Home.js";
import ProductDetails from "./component/Product/ProductDetails.js";
import Products from "./component/Product/Products.js";
import Search from "./component/Product/Search.js";
import LoginSignUp from './component/User/LoginSignUp';
import store from "./store";
import { loadUser } from './action/userAction';
import UserOption from "./component/layout/Header/UserOption.js";
import { useSelector } from 'react-redux';
import Profile from "./component/User/Profile.js"
import ProtectedRoute from './component/Route/ProtectedRoute';
function App() {
const {isAuthenticated, user} = useSelector(state => state.user)
React.useEffect(() => {
webFont.load({
google:{
families:["Roboto","Droid Sans","Chilanka"]
},
});
store.dispatch(loadUser())
}, [])
return (
<Router>
<Header />
{isAuthenticated && <UserOption user={user} />}
<Routes>
<Route path="/" element={<Home />} />
<Route path="/product/:id" element={<ProductDetails />} />
<Route path="/products" element={<Products />} />
<Route path="/products/:keyword" element={<Products />} />
<Route path="/search" element={<Search />} />
<Route path="/account" element={ <ProtectedRoute> <Profile /> </ProtectedRoute> } />
<Route path="/login" element={<LoginSignUp />} />
</Routes>
<Footer />
</Router>
);
}
export default App;
In your App.js you can declare the protected route like this
<Route path="/account" element={ <ProtectedRoute /> } >
<Route path="/account" element={ <Profile /> } >
</Route>
You can use Outlet of react-router v6 for passing the Component
const ProtectedRoute = ({ element: Element, ...rest }) => {
const { loading, isAuthenticated, user } = useSelector(state => state.user);
if (loading) {
return <h2>Loading...</h2>
}
return isAuthenticated ? <Outlet /> : <Navigate to="/login" />;
}
# Sayedul Karim.
I have a better and more concise way for this, An example of the code is below.
<Routes>
<Route element={<App />}>
{isAuthenticated ? (
<>
<Route path="/*" element={<PrivateRoutes />} />
<Route
index
element={<Navigate to="/account" />}
/>
</>
) : (
<>
<Route path="auth/*" element={<AuthPage />} />
<Route path="*" element={<Navigate to="/auth" />} />
</>
)}
</Route>
</Routes>
In this way, you don't have to make any private component instead just make a component for private routes where the routes are defined.
The PrivateRoutes component will be like this
<Routes>
<Route>
{/* Redirect to account page after successful login */}
{/* Pages */}
<Route path="auth/*" element={<Navigate to="/account" />} />
<Route path="account" element={<Account />} />
</Routes>
If any query further, feel free to ask....
I'm not sure, but you haven't pass any "element" prop to your ProtectedRoute component. You pass Profile component as children, so try render children instead of element in ProtectedRoute if you want your code to work like this.
I believe that you might want not to nest those routes, so also you might want to try use ProtectedRoute as Route in your router, I'm talking about something like this
<Routes>
...
<ProtectedRoute path="/account" element={<Profile />} />
...
</Routes>
UPDATE
It might show you this error because your Route is conditionally rendered, so try to handle loading state in some other way, maybe something like this
return (
<Route
{...rest}
render={(props) => {
if(!isAuthenticated) {
return redirect("/login")
}
if(loading) {
return <LoadingComponent />
}
return <Element {...props} />
}}
/>
)
I am learning connecting MongoDB Realm to react by following this article. The problem with this article is that it is outdated, and the newer version of react doesn't support component = {Home} in react-router and perhaps not the render = {()={}} also.
When I shamelessly copy-pasted all code and then ran it I got this warning
index.js:21 Matched leaf route at location "/" does not have an element. This means it will render an with a null value by default resulting in an "empty" page.
then I changed the code(a line) for the Home page just for testing to this
<Route path="/" element={()=><MongoContext.Consumer>{(mongoContext) => <Home mongoContext={mongoContext}/>}</MongoContext.Consumer>} />
Then I got a new warning, LOL!
Warning
Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
I have no what to do now. So, if anyone knows how to solve this, then it will be helpful for me.
App.js
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from "./pages/Home"
import * as Realm from 'realm-web'
import Authentication from './pages/Authentication';
import LogOut from './pages/Logout';
import Navigation from './components/Navigation';
import MongoContext from './MongoContext';
import 'bootstrap/dist/css/bootstrap.min.css'
import { Container } from "react-bootstrap"
import { useEffect, useState } from 'react';
function renderComponent (Component, additionalProps = {}) {
return <MongoContext.Consumer>{(mongoContext) => <Component mongoContext={mongoContext} {...additionalProps} />}</MongoContext.Consumer>
}
function App() {
const [client, setClient] = useState(null)
const [user, setUser] = useState(null)
const [app, setApp] = useState(new Realm.App({ id: "restaurant_app-qbafd" }))
useEffect(() => {
async function init() {
if (!user) {
setUser(app.currentUser ? app.currentUser : await app.logIn(Realm.Credentials.anonymous()))
}
if (!client) {
setClient(app.currentUser.mongoClient('mongodb-atlas'))
}
}
init();
}, [app, client, user])
return (
<BrowserRouter>
<Navigation user={user} />
<MongoContext.Provider value={{ app, client, user, setClient, setUser, setApp }}>
<div className="App">
<header className="App-header">
<Routes>
<Route path="/signup" render={() => renderComponent(Authentication, {type: 'create'})} />
<Route path="/signin" render={() => renderComponent(Authentication)} />
<Route path="/logout" render={() => renderComponent(LogOut)} />
<Route path="/" element={()=><MongoContext.Consumer>{(mongoContext) => <Home mongoContext={mongoContext}/>}</MongoContext.Consumer>} />
</Routes>
</header>
</div>
</MongoContext.Provider>
</BrowserRouter>
);
}
export default App;
Try to wrap all your routes in the MongoContext.Consumer:
<BrowserRouter>
<Navigation user={user} />
<MongoContext.Provider
value={{ app, client, user, setClient, setUser, setApp }}
>
<MongoContext.Consumer>
{(mongoContext) => (
<div className='App'>
<header className='App-header'>
<Routes>
<Route
path='/signup'
element={
<Authentication mongoContext={mongoContext} type='create' />
}
/>
<Route
path='/signin'
element={<Authentication mongoContext={mongoContext} />}
/>
<Route
path='/logout'
element={<LogOut mongoContext={mongoContext} />}
/>
<Route path='/' element={<Home mongoContext={mongoContext} />} />
</Routes>
</header>
</div>
)}
</MongoContext.Consumer>
</MongoContext.Provider>
</BrowserRouter>;
I am working on application where application have two parts one for public and for admin users. I completed for public side but now I am am facing issue on admin side because when I render my 2nd menu for admin public side menu is also showing . Could someone please help me how to handle just 2nd menu let suppose if user hit ( /admin/home ) then I need to show 2nd menu if user hit (/home) then I want to show Ist menu.
Admin Route
<TopMenu>
<PrivateRoute
exact
auth={auth}
path="/dashboard"
currentUser={"admin" || null}
roles={["admin"]}
component={Dashboard}
/>
</TopMenu>
Public Route
<Route exact path="/" render={(props) => <Home {...props} />} />
<Route
exact
path="/about"
render={(props) => <About {...props} />}
/>
<Route
exact
path="/contact"
render={(props) => <Contact {...props} />}
/>
create a Private Route file and check if user is authenticated if yes show that component and if not redirect to some other route
You can do it like this or implement your own way
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
export const PrivateRoute = ({
isAuthenticated,
component: Component,
...rest
}) => (
<Route {...rest} component={(props) => (
isAuthenticated ? (
<div>
<Component {...props} />
</div>
) : (
<Redirect to="/" />
)
)} />
);
export default PrivateRoute
and in the main router file you can import the Private Route and use it like this
import PublicRoute from './routers/PublicRoute';
import Appp from './components/Appp';
import PrivateRoute from './routers/PrivateRoute';
const store = configureStore();
function App() {
return (
<div className="App">
<Provider store={store}>
<Switch>
<PublicRoute path="/" component={HomePage} exact={true} />
<PrivateRoute path="/yourPrivateRoute" component={yourComponent}/>
</Switch>
</Provider>
</div>
);
}
export default App;
I am using react router v4 for routing. The layout my app is, there is homepage which is for end user. The path for homepage is obviously /. There is dashboard section too. One for admin, one for agent and another for owner. They have their own layout from top to bottom. For homepage its working. But when i hit url /admin/dashboard, the main page of admin dasboard is not shown. The same homepage is shown.
Here is what i have done
const AsyncRoute = ({ load, ...others }) => (
<Route
{...others} render={(props) => (
<Bundle load={load} {...props} />
)}
/>
);
app.js
const render = messages => {
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
</Provider>,
document.getElementById("app")
);
};
import Routes from 'routes';
class App extends React.Component {
render() {
return (
<div>
<Navbar userForm={this.handleDialog} />
<Routes />
</div>
);
}
}
routes.js
function Routes({ location }) {
return (
<Switch location={location}>
<AsyncRoute exact path="/" load={loadHomePage} />
<AsyncRoute exact path="/features" load={loadFeaturePage} />
<AsyncRoute path="" load={loadNotFoundPage} />
</Switch>
);
}
I want now admin dashboard a complete new page with different layout not children of App component so i did
the following
class AdminDashboard extends React.Component {
render() {
return (
<div>
<TopNavigation />
<SideNavigation />{/* it will have link */}
<Routes /> {/* it will display page */}
</div>
);
}
}
function AdminRoutes({ location }) {
return (
<Switch location={location}>
<AsyncRoute
exact
path="/admin/dashboard"
load={loadAdminDashboardPage}
/>
<AsyncRoute exact path="/commission" load={loadCommissionPage} />
<AsyncRoute path="" load={loadNotFoundPage} />
</Switch>
);
}
when i hit the url /admin/dashboard i get the app page not the admin dashboard page and same with the
/commission which should be a child of AdminDashboard
How can i make my router work for different layout?
From Meteorchef Base, dedicated routing according to the user (loggingIn prop in this case):
Public.js, only for non admin, redirect if not :
import React from 'react';
import PropTypes from 'prop-types';
import { Route, Redirect } from 'react-router-dom';
const Public = ({ loggingIn, authenticated, component, ...rest }) => (
<Route {...rest} render={(props) => {
if (loggingIn) return <div></div>;
return !authenticated ?
(React.createElement(component, { ...props, loggingIn, authenticated })) :
(<Redirect to="/account" />);
}} />
);
Public.propTypes = {
loggingIn: PropTypes.bool,
authenticated: PropTypes.bool,
component: PropTypes.func,
};
export default Public;
Authenticated.js, only for admin, also redirect if not :
import React from 'react';
import PropTypes from 'prop-types';
import { Route, Redirect } from 'react-router-dom';
const Authenticated = ({ loggingIn, authenticated, component, ...rest }) => (
<Route {...rest} render={(props) => {
if (loggingIn) return <div></div>;
return authenticated ?
(React.createElement(component, { ...props, loggingIn, authenticated })) :
(<Redirect to="/login" />);
}} />
);
Authenticated.propTypes = {
loggingIn: PropTypes.bool,
authenticated: PropTypes.bool,
component: PropTypes.func,
};
export default Authenticated;
then in your App, (stateless in this case, but you can create a class as well too) :
// import everything
const App = appProps => (
<Router>
<div>
<Switch>
<Route exact name="index" path="/" component={Home} />
<Authenticated exact path="/account" component={Account} {...appProps} />
<Public path="/signup" component={Signup} {...appProps} />
<Public path="/login" component={Login} {...appProps} />
<Route component={NotFound} />
</Switch>
</div>
</Router>
);
App.propTypes = {
loggingIn: PropTypes.bool,
authenticated: PropTypes.bool,
};