I have created a Link on a Post that links to the Post Page. When the link is clicked with the url at '/' the link doesn't work correctly, it displays the correct link in the url e.g. http://localhost:3000/posts/624a771b42211849eaada885 but the page doesn't redirect, the only way it works is if I refresh the page. However, if I am on my Popular Posts page the link works correctly. To be clear the Post is in a component called Posts which displays all of the Posts. The Posts component is a shared component across many components, such as Home page ('/') and Popular Posts (/popular) the link works in all other pages except for when at '/'
Below is the Link.
<Link to={`/posts/${_id}`}>
<h2 className='post-title'>{title}</h2>
</Link>
My routes are set up with the following:
<Route element={!token ? <Navigate replace to='/login' /> : <Navbar />}>
<Route
path='/'
element={<Home />}
>
<Route
path='/popular'
element={<Popular />}
/>
<Route
path='/posts/:postId'
element={<PostPage />}
/>
</ Route>
</Route>
In my Navbar I have:
const Navbar = () => {
return(
<>
<nav>
</nav>
<Outlet />
</>
)
}
and finally, in my Home.js I have this:
const Home = () => {
return (
<div>content</div>
<div>content</div>
<div className='home-posts-container'>
{window.location.pathname === '/' ? <PopularPosts /> :
<Outlet />}
</div>
<div>content</div>
)
}
From what I can tell of your Home component with
const Home = () => {
return (
<>
<div>content</div>
<div>content</div>
<div className="home-posts-container">
{window.location.pathname === "/" ? <PopularPosts /> : <Outlet />}
</div>
<div>content</div>
</>
);
};
You want to render the PopularPosts component exactly when the path is "/", otherwise you want to render one of the matched nested routes.
The issue is that with the above implementation the Outlet isn't rendered when the path changes so none of the nested routes are matchable.
It appears you want the Home component to be a layout route, it should render all the div elements and content, and just the Outlet. Move the PopularPosts component into an index route.
const Home = () => {
return (
<>
<div>content</div>
<div>content</div>
<div className="home-posts-container">
<Outlet />
</div>
<div>content</div>
</>
);
};
...
<Routes>
<Route element={!token ? <Navigate replace to="/login" /> : <Navbar />}>
<Route path="/" element={<Home />}>
<Route index element={<PopularPosts />} />
<Route path="/popular" element={<Popular />} />
<Route path="/posts/:postId" element={<PostPage />} />
</Route>
</Route>
</Routes>
For more information see:
Layout routes
Index routes
Related
I have set up a login page for my web app but the site header component I created earlier appears on the top. It has a menu in it that leads to other pages, so if it's on the login page a user doesn't need to login when they can just click the menu option that will lead them to the home page.
I would like the site header to be invisible on the login, register and reset pages.
index.js
return (
<BrowserRouter>
<SiteHeader />
<Routes>
<Route path="/reviews/:id" element={ <MovieReviewPage /> } />
<Route path="/movies/home" element={<HomePage />} />
<Route path="/movies/favorites" element={<FavoriteMoviesPage />} />
<Route path="/movies/upcoming" element={<UpcomingMoviesPage />} />
<Route path="/movies/:id" element={<MoviePage />} />
<Route exact path="/" element={<LoginPage />} />
<Route exact path="/register" element={<RegisterPage />} />
<Route exact path="/reset" element={<ResetPage />} />
<Route path="*" element={ <Navigate to="/" /> } />
</Routes>
</BrowserRouter>
);
};
There are many ways to do that, but personally, I prefer to do so.
export const MyLayout = ({children}) => {
return (
<>
<SiteHeader />
{children}
</>
)
}
And after all, put your PageComponent inside component wherever you need.
For example.
const HomePage = () => {
return (
<MyLayout>
<div> This is Home Page </div>
</MyLayout>
)
}
Add an Authentication Context Provider. You could then read the context and render the header when you're logged in, else not.
https://codesandbox.io/embed/authentication-with-react-context-d3x0r
I need to create 2 multi-pages UIs(managementUI and documentUI) with different headers.
updates:
the header contains navigation buttons to open and display different subpage components between the header and footer of the UI it belongs to.
e.g. the ApiCardGrid component will be displayed in managementUI.
However, the subpage cannot open in the outlet between header and footer when I clicked button in navigation header.
in App.tsx
function App() {
const [totalApiData, setTotalApiData] = useState([]);
useEffect(() => {
axios.get("/api-documents").then((res) => {
setTotalApiData(res.data);
});
}, []);
return (
<>
<div className="App">
<Router>
<Routes>
<Route
path="/apiManagement"
element={<ManagementUI />}
>
<Route
path="apis"
element={<ApiCardGrid spacing={2} size={3} />}
/>
</Route>
<Route
path="/documents"
element={<DocumentUI />}
></Route>
</Routes>
</Router>
</div>
</>
);
}
(update) Following comments made by #Drew Reese, in ManagementUI, I put an Outlet between header and footer to render the contents of subpages like ApiCardGrid. In ManagementUI.tsx:
function ManagementUI() {
const [totalApiData, setTotalApiData] = useState([]);
useEffect(() => {
axios.get("/api-documents").then((res) => {
setTotalApiData(res.data);
});
}, []);
return (
<>
<div className="management-ui">
<div className="management-header">
<Header />
</div>
<div className="management-content">
<Outlet />
</div>
<div className="management-footer">
<Footer />
</div>
</div>
</>
);
}
(update)API List button that link to /apis to display ApiCardGrid component in the Header:
<Header>
<Toolbar
disableGutters
variant="dense"
id="header-primary-navigation"
className="gds-primary-navigation"
>
<nav>
<Button className="gds-button-primary-navigation" href="/apiManagement/apis">
API List
</Button>
<Link to="/apiManagement/apis">API List</Link>
<Button className="gds-button-primary-navigation" href="/apiInfo">
API Info
</Button>
<Button className="gds-button-primary-navigation" href="/addApis">
Add API
</Button>
<Button
className="gds-button-primary-navigation active"
href="/active"
>
active page
</Button>
</nav>
</Toolbar>
</ Header>
similar in Header2
However, when I open UI1 localhost:3000/UI1, the UI1 opened successfully, but if I click button subpage1 to try to display subpage1 in UI1, the console responds error:"No routes matched location "/subpage1".(solved following answer of #Drew Reese)
update:
When I input url http://localhost:3000/apiManagement, the UI shows up. However, when I clicked the API List button, the url jumps to http://localhost:3000/apiManagement/apis, but the subpage item not shows up. I opened inspect tool, but no errors in console.
The subpage(ApiGridCard) supposed to display like
When rendering components on routes that render descendent routes the parent routes necessarily need to append the path wildcard "*" to their paths to allow descendent path matching.
Example:
<Router>
<Routes>
<Route
path="/UI1/*"
element={<UI1 />}
/>
<Route
path="/UI2/*"
element={<UI2 />}
/>
</Routes>
</Router>
An alternative is to create UI layout routes that render the appropriate header component and an Outlet for nested routes to render their element into.
Example:
import { Outlet } from 'react-router-dom';
const UI1 = () => (
<div >
<div >
<Header1 />
</div>
<div >
<Outlet />
</div>
<div >
<Footer />
</div>
</div>
);
const UI2 = () => (
<div >
<div >
<Header2 />
</div>
<div >
<Outlet />
</div>
<div >
<Footer />
</div>
</div>
);
...
<Router>
<Routes>
<Route path="/UI1" element={<UI1 />}>
<Route path="subpage1" element={<Subpage1 />} /> // "/UI1/subpage1"
<Route path="subpage2" element={<Subpage2 />} /> // "/UI1/subpage2"
</Route>
<Route path="/UI2" element={<UI2 />}>
<Route path="subpage3" element={<Subpage3 />} /> // "/UI2/subpage3"
<Route path="subpage4" element={<Subpage4 />} /> // "/UI2/subpage4"
</Route>
</Routes>
</Router>
For following scenario, I have to redirect to home page if entered url not found.
When user try to access page which is not available, it should redirect to home page or error page.
CODE:
*
function App() {
return (
<div className="app">
<Suspense fallback={<div>Loading...</div>}>
<Router history={history}>
<Header />
<div className="app-container">
<Switch>
{routes.map(
({ key, path, crumbs, renderComponent }) => {
return (
<Route key={key} path={path}>
{renderComponent(crumbs)}
</Route>
)
}
)}
<Redirect exact from="/" to="/merchants" />
</Switch>
</div>
<div className="footer-wrapper">
<div className="footer">
<footer max-width="1920"></footer>
</div>
</div>
</Router>
</Suspense>
</div>
)
}
Add another Redirect that handles any route path that wasn't handled above it by a Route or Redirect component rendering on a more specific path.
<Switch>
{routes.map(({ key, path, crumbs, renderComponent }) => (
<Route key={key} path={path}>
{renderComponent(crumbs)}
</Route>
))}
<Redirect exact from="/" to="/merchants" />
<Redirect to="/" />
</Switch>
Since it seems the "/merchants" route is your default homepage you can remove the exact and to props and handle any route path that wasn't handled previously.
<Switch>
{routes.map(({ key, path, crumbs, renderComponent }) => (
<Route key={key} path={path}>
{renderComponent(crumbs)}
</Route>
))}
<Redirect to="/merchants" />
</Switch>
Currently using ReactJS to construct a small web app. I have the following parent function:
const Main = () => {
return (
<div className="dialog-base">
<BrowserRouter>
<Switch>
<Route exact path="/login" component={Login}></Route>
<Route exact path="/login/forgot_password" component={ForgotPwd}></Route>
<Route exact path="/login/reset_password/:key" component={ResetPwd}></Route>
<Route exact path="/portal" component={Portal}></Route>
</Switch>
</BrowserRouter>
</div>
);
}
and the following is the "Portal" component:
class Portal extends React.Component {
render = () => {
return (
<BrowserRouter basename="/main">
<div className="navmenu">
<NavLink to="messaging" activeClassName="selected">Messaging</NavLink>
<NavLink to="files" activeClassName="selected"></NavLink>
<NavLink to="payledger" activeClassName="selected"></NavLink>
</div>
<div className="apparea">
<Switch>
<Route path="/messaging" component={Messaging}></Route>
<Route path="/files" component={Files}></Route>
<Route path="/payledger" component={PayLedger}></Route>
</Switch>
</div>
</BrowserRouter>
);
}
}
When the portal component is loaded and I refresh the web page, the page goes blank. I am assuming that this has something to do with the nested routing? Any help on how to fix it would be much appreciated.
You don't need two <BrowserRouter />. Just define one <BrowserRouter /> in your top level component.
In react-router-dom v4+ the <Route /> is just like a regular component and you can use it inside your components to render UI when the path matches the URL.
Here is the working codesandbox example.
Make sure not to put exact on your parent <Route /> because when you have child routes like /main/messaging the <Route exact path="/main" /> never gets to render and therefore children of that route can't be rendered also.
You keep your <Main /> component as is but remove the exact from the <Route path='/portal' /> and change the <Portal />.
class Portal extends React.Component {
render = () => {
return (
<React.Fragment>
<div className="navmenu">
<NavLink to="/portal/messaging" activeClassName="selected">Messaging</NavLink>
<NavLink to="/portal/files" activeClassName="selected"></NavLink>
<NavLink to="/portal/payledger" activeClassName="selected"></NavLink>
</div>
<div className="apparea">
<Switch>
<Route path="/portal/messaging" component={Messaging}></Route>
<Route path="/portal/files" component={Files}></Route>
<Route path="/portal/payledger" component={PayLedger}></Route>
</Switch>
</div>
</React.Fragment>
);
}
}
I have a react application in which I have wrapped layout components for the other routes, the thing is when I click the links present in the sidebar(part of layout) they are not being rendered on the screen, here is my code.
App.js
//Imports here
<Provider store={store}>
<Router>
<Switch>
<Layout>
<Route exact path="/admin" render={() => <Admin />} />
<Route exact path="/employees" render={() => <Employees />} />
<Route exact path="/profile" component={Profile} />
</Layout>
<Switch>
</Router>
</Provider>
Layout.js
//imports here
//styling here
<Link to='/employees' />
// and likewise for rest of the routes
When clicking the links ie, employees or profile they aren't being rendered, tried console.log to see if my layout was obstructing that, but no use. Please help me
It should be inside the Switch component but you can wrap it with a Layout component like that.
const Headers = () => (
<Layout>
<ul>
<li>
<Link to="/admin">Admin</Link>
</li>
<li>
<Link to="/profile">Profile</Link>
</li>
<li>
<Link to="/employees">Employees</Link>
</li>
</ul>
</Layout>
);
function App() {
return (
<Router>
<Layout>
<Header></Header>
<Switch>
<Route exact path="/admin" render={() => <Admin />} />
<Route exact path="/employees" render={() => <Employees/>}/>
<Route exact path="/profile" component={Profile} />
</Switch>
</Layout>
</Router>
);
}
If your URL is changing but the content is not being rendered, the problem is this, apart from wrapping the Routes as mentioned in #G.aziz 's answer since the routes are children WRT layout components we have to use {props.children} inside the layout component to render the content like so...
Layout.jsx
<div>
<Sidebar />
<Navbar />
{props.children} // here we are rendering the routes which we mentioned in the switch component in App.js
</div>
For me this solution fixed. Also please refer this question for further information. React-router v4, URL changing but component doesn't render