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>
Related
I'm building a winery themed shopping app in React and I'm using functional components. I have 12 products (wines) displayed on one page, and I want to make a separate page for each wine when the user clicks on it.
This is how I handled routes:
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products wines={products}/>} />
<Route path="/contact" element={<Contact />} />
<Route path="/cart" element={<Cart />} />
{wines.map((wine) => (
<Route key={wine.id} path={`/shop/${wine.id}`} element={<Item item={wine} />}/>
))}
</Routes>
</BrowserRouter>
I mapped through all of the wines and created a separate route for each of them, and sent a particular wine as a prop to each "Item" component.
Item component looks like this:
const Item = (item) => {
return (
<>
<Header />
<div className="item_container">
<img className="item_image" alt={item.name} src={item.image} />
<h1 className="item_name">{item.name}</h1>
<h3 className="item_price">{item.price}</h3>
</div>
<Footer />
</>
)
When I console log "item" in this component, I get the specific wine and I can see all of its attributes (name, price, image URL), but when I try to access those attributes in order to display them (item.name, item.image, item.price), the console logs "undefined".
How can i fix this?
You've passed a item prop to the Item component.
<Item item={wine} />
And then named the props object item in the Item component.
const Item = (item) => { ... }
Either rename to props and correctly access props.item.xxx
const Item = (props) => {
const { item } = props;
return (
<>
<Header />
<div className="item_container">
<img className="item_image" alt={item.name} src={item.image} />
<h1 className="item_name">{item.name}</h1>
<h3 className="item_price">{item.price}</h3>
</div>
<Footer />
</>
);
}
or destructure item directly
const Item = ({ item }) => (
<>
<Header />
<div className="item_container">
<img className="item_image" alt={item.name} src={item.image} />
<h1 className="item_name">{item.name}</h1>
<h3 className="item_price">{item.price}</h3>
</div>
<Footer />
</>
);
An optimization I'd suggest is to declare a single route with the wine id as a route path param, and then access the wines array in Item.
Example:
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products wines={products}/>} />
<Route path="/contact" element={<Contact />} />
<Route path="/cart" element={<Cart />} />
<Route path="/shop/:wineId" element={<Item />} />
</Routes>
</BrowserRouter>
...
import { useParams } from 'react-router-dom';
const Item = () => {
const { wineId } = useParams();
const wine = wines.find(wine => wine.id === wineId);
if (!wine) return <div>No wine found.</div>;
return (
<>
<Header />
<div className="item_container">
<img className="item_image" alt={wine.name} src={wine.image} />
<h1 className="item_name">{wine.name}</h1>
<h3 className="item_price">{wine.price}</h3>
</div>
<Footer />
</>
);
}
Please check null and empty array for "wines" first , if not null then map through "wines"
New to react, So a little confused on why this was giving me an error.
This is my main.js file
const Main = () => (
<div>
<Menu pointing secondary stackable>
<Menu.Menu position='right'>
<Menu.Item active as={NavLink} exact to='/' name='Welcome' />
<Menu.Item as={NavLink} to='/powersection' name='power' />
</Menu.Menu>
</Menu>
<Segment raised className='magentaSegment'>
<Routes>
<Route exact path='/' element={<HomeView />} />
<Route path='/powersection' element={<PowerSection />} />
</Routes>
</Segment>
</div>
)
export default Main
This is what my App.js file looks like
function App() {
return (
<div id='container'>
<div id='main'>
<Routes>
<Route path='/' element={ <Main />} />
</Routes>
</div>
<div id='footer'>
<Footer />
</div>
</div>
);
}
export default App;
And this is my index.js file.
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
reportWebVitals();
When I toggle to the Power menu item. this is the error i get
index.js:44 No routes matched location "/powersection"
Anyone know how i can fix this? Thank you
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
Sorry, extreme React newbie. I have a simple react function component:
import React from "react";
export default function Portal() {
return (
<h2>Portal</h2>
);
}
In my App.js I have:
import React from 'react';
import { BrowserRouter, Route, Switch } from "react-router-dom";
import './App.css';
import Portal from "./components/Portal/portal";
import LogoHeader from './components/LogoHeader';
import NewFooter from "./components/NewFooter";
function App() {
return (
<div className="App">
<BrowserRouter>
<Switch>
<Route path="/portal">
<Portal />
</Route>
</Switch>
</BrowserRouter>
...
<LogoHeader />
...
<NewFooter/>
</div>
);
}
export default App;
In LogoHeader I have a link:
<a href='/Portal'>Portal</a>
When I click the "Portal" link it refreshes the page, but it now displays the "Portal" header at the top and then the rest of the App.js page. How do I make Portal be a single page all on it own?
UPDATE
Based on an Answer given by #DrewReese I have changed App.js Render to:
return (
<div className="App">
<BrowserRouter>
<p className="greeting">Hello, {token.firstName}</p>
<LogoHeader />
<GetCategories />
<Navbar id="customNav" navItems={navItems} shopCategories={shopCategories} />
<Switch>
<Route path="/home">
<Slideshow id="slideshow" />
<div id="productContainer">
<br />
<h3>Featured Products</h3>
<br />
<FeaturedCards />
<br />
<h3>Most Popular</h3>
<br />
<ProdCard />
<br />
<h3>New Products</h3>
<br />
<ProdCard />
</div>
</Route>
<Route path="/portal">
<Portal />
</Route>
</Switch>
<NewFooter />
</BrowserRouter>
</div>
);
When you use an anchor tag (<a href="...." />) it will trigger a page reload, which reloads your React app. You should use the Link component to link to pages within your app.
LogoHeader
import { Link } from 'react-router-dom';
...
<Link to='/portal'>Portal</Link>
If you want the header to display rendered pages then move it up in the JSX. Remember, any links you render need to be rendered within a routing context, so the header should be rendered within the BrowserRouter component.
App
function App() {
return (
<div className="App">
<BrowserRouter>
<LogoHeader />
<Switch>
<Route path="/portal">
<Portal />
</Route>
</Switch>
<NewFooter/>
</BrowserRouter>
</div>
);
}
If you want the Portal page to be rendered and the header not to be rendered, then you can render a custom header and conditionally render LogoHeader based on route matching.
useRouteMatch
matchPath props
It returns null when provided pathname does not match path prop.
const Header = () => {
const match = useRouteMatch("/portal");
return !match ? <LogoHeader /> : null;
}
function App() {
return (
<div className="App">
<BrowserRouter>
<Header />
<Switch>
<Route path="/portal">
<Portal />
</Route>
</Switch>
<NewFooter/>
</BrowserRouter>
</div>
);
}
Update
Within the Switch component, path order and specificity matter. Order more specific paths before less specific paths. "/portal" is more specific than "/" so it should be rendered earlier. This is because the Switch component exclusively matches and renders routes (i.e. only the first match found), as opposed to the Router that inclusively renders them (i.e. all matches).
return (
<div className="App">
<BrowserRouter>
<p className="greeting">Hello, {token.firstName}</p>
<LogoHeader />
<GetCategories />
<Navbar id="customNav" navItems={navItems} shopCategories={shopCategories} />
<Switch>
<Route path="/portal">
<Portal />
</Route>
<Route path="/">
<Slideshow id="slideshow" />
<div id="productContainer">
<br />
<h3>Featured Products</h3>
<br />
<FeaturedCards />
<br />
<h3>Most Popular</h3>
<br />
<ProdCard />
<br />
<h3>New Products</h3>
<br />
<ProdCard />
</div>
</Route>
</Switch>
<NewFooter />
</BrowserRouter>
</div>
);
I suppose you just need to use <Link to="/portal"> from react-router here.
import { Link } from 'react-router-dom' (v4 of react-router)
import { Link } from 'react-router' (v3 of react-router)
There are 2 issues with using a tag:
it does contains relative urls (for this particular case it won't be a problem, but it confuse you on some pages).
it triggers page refresh
More info about using Link
https://reactrouter.com/web/guides/quick-start
consider the following example, I have a login page and an admin page. After logging in we will be redirected to admin page.
The admin page is shown as follows.
Desired behaviour: To render cars component in the admin component itself
Actual behaviour: On clicking cars or bikes component they are being rendered on a different page.
code is as follows
App.js
//imports here
function App() {
return(
<Router>
<Switch>
<Route exact path="/" component={Login} />
<Route exact path="/admin" component={Admin} />
<Route exact path="/cars" component={Cars} />
<Route exact path="/bikes" component={Bikes} />
</Switch>
</Router>
);
}
Admin.js
//imports here
const Admin = () => {
return (
<Fragment>
<div className="is-flex">
<Sidebar />
<Navbar />
</div>
</Fragment>
);
};
navbar.js
// imports here
const Sidebar = () => {
return (
<aside className="aside">
<p className="menu-label">Test Routes</p>
<ul className="menu-list">
<li>
<Link to="/cars">Cars</Link>
</li>
<li>
<Link to="/bikes">Bikes</Link>
</li>
</ul>
</aside>
);
};
Using react-router-dom ^5.1.2
Tried this but not able to understand what I missed? how to solve this problem?
Move your default route to the bottom of the stack. i.e.
function App() {
return(
<Router>
<Switch>
<Route path="/admin" component={Admin} />
<Route path="/cars" component={Cars} />
<Route path="/bikes" component={Bikes} />
<Route exact path="/" component={Login} />
</Switch>
</Router>
);
}