How to conditionally render wrapping components in React Router 4/5 - javascript

I am migrating an app from react-router version 3 to 5, and am a little stuck trying to figure out the best approach to the following problem.
I have some routes that make use of a wrapping component.
in routerv3 I could do things like
const Container = props =>
<div>
<header>container1</header>
{ props.children }
</div>
const Container2 = props =>
<div>
<header>container2</header>
{ props.children }
</div>
These containers have other function
<Route component={Container}>
<Route path='/container1' component={Page1} />
</Route>
<Route component={Container2}>
<Route path='/container2' component={Page1} />
</Route>
When navigating to /container1 it would render the Page1 component with a Container1 wrapping it, and then on /container2 it would render Page1 with Container2 wrapping it.
However when moving to router v4+ all matching routes are rendered, so what happens is that for example on /container2 the result is
<div>
<header>container1</header>
</div>
<div>
<header>container2</header>
<Page1 />
</div>
Does anyone know of a way to write these "wrapping components" such that they are only rendered if routes match? Or perhaps a better way to do this kind of thing if that's not how react-router 4/5 works.
here is a link to a stackblitz of me trying to figure this out
https://stackblitz.com/edit/react-jvsdsn?file=index.js

You can do something like this
<Route
path="/container1"
render={(routeProps) => (
<Container>
<Page1 {...routeProps} />
</Container>
)}
/>
<Route
path="/container2"
render={(routeProps) => (
<Container2>
<Page1 {...routeProps} />
</Container2>
)}
/>

Related

A route is not covered in layout component, but still renders layout in React

I'm having to trouble making a route without layout component. Please check my code first.
// App.js
const App = () => {
return (
<div className={styles.container}>
<Header />
<main>
<QuizBoxContainer>
<Routes>
<Route path='/' element={<QuizSelect />} />
<Route path='quiz-for/:language' element={<QuizCard />} />
<Route path='result' element={<ResultPage />} />
</Routes>
</QuizBoxContainer>
<Routes>
<Route path='wrong-answer' element={<WrongAnswer />} />
</Routes>
</main>
</div>
)
}
In the code, <QuizBoxContainer> is the layout component.
What I was trying to do is making another path,
<Routes>
<Route path='wrong-answer' element={<WrongAnswer />} />
</Routes>
outside of <QuizBoxContainer> so I can use another layout on this component.
However, when I go to WrongAnswer,
It still renders.
What is wrong with my code, and how can I solve it?
Issue
The is because the QuizBoxContainer layout wrapper component is not rendered on any route, it's always rendered.
Solution
Move QuizBoxContainer into a layout route. You'll need to update QuizBoxContainer so it renders an Outlet component instead of the children prop.
Example:
import { Outlet } from 'react-router-dom';
const QuizBoxContainer = () => {
...
return (
... quiz container layout/styling ...
<Outlet /> // <-- nested routes render content here
...
);
};
Render QuizBoxContainer on a layout route wrapping the routes you want to render within it, render the wrong answer route outside the layout route.
const App = () => {
return (
<div className={styles.container}>
<Header />
<main>
<Routes>
<Route element={<QuizBoxContainer />}>
<Route path='/' element={<QuizSelect />} />
<Route path='quiz-for/:language' element={<QuizCard />} />
<Route path='result' element={<ResultPage />} />
</Route>
<Route path='wrong-answer' element={<WrongAnswer />} />
</Routes>
</main>
</div>
)
}

React Router v5: Route defined in child component not working

My application is using react-router-dom v5.3 and I'm having trouble routing from the root url of my application to a child component (called the "See All" Page) while also passing props down. Currently, my code just renders an empty page whenever I navigate to the child component.
RootRouter.js:
export default function RootRouter() {
return (
<BrowserRouter>
<Switch>
<Route
path="/"
exact
render={() => <HomeView />}
/>
</Switch>
</BrowserRouter>
);
}
Homeview.js:
function HomeView() {
const seeAllViewTitle = "some_title_here"
return (
<div>
<div>Some content here!</div>
<Link to={`/seeall/${seeAllViewTitle}`}}>
<Button/>
</Link>
<Route path={`/seeall/${seeAllViewTitle}`}>
<SeeAllView
groupTitle={""}
pageData={[]}
eventHandler={some_function_here}
/>
</Route>
</div>
);
}
If I were to put the Route that is currently in homeview.js inside of Rootrouter.js, the component shows up, but I can't pass any props into it from there.
Issue
The HomeView component is rendered only when the path is exactly "/". When the link is clicked and navigates to "/seeall/some_title_here " the path no longer exactly matches and the HomeView component unmounts.
Solution
Remove the exact prop from the root route so nested/sub routes can also be matched and rendered.
export default function RootRouter() {
return (
<BrowserRouter>
<Switch>
<Route path="/" component={HomeView} />
</Switch>
</BrowserRouter>
);
}
If you did not intend for these components to be rendered at the same time then move the nested route out to the RootRouter component.
export default function RootRouter() {
return (
<BrowserRouter>
<Switch>
<Route path="/seeall/:title">
<SeeAllView
groupTitle={""}
pageData={[]}
eventHandler={some_function_here}
/>
</Route>
<Route path="/" component={HomeView} />
</Switch>
</BrowserRouter>
);
}
...
function HomeView() {
const seeAllViewTitle = "some_title_here"
return (
<div>
<div>Some content here!</div>
<Link to={`/seeall/${seeAllViewTitle}`}}>
<Button/>
</Link>
</div>
);
}
Are you remembering to receive props in the function declaration for HomeView? usually, you'll need to explicitly define that you are receiving props, either with a props variable or by defining specific prop names in an object syntax

Is there a best practice way to hide component using react router?

To hide the navbar on the home component I am doing the following
const NavbarComponent = (props) => {
console.log(props);
if (props.match.path === '/') {
return null;
} else
return (
it works fine, I need to have access to the router so I can send people to locations dependant on the props object , is there a better way to do it such that I have all router logic in the same place?
this is the current state of my router
return (
<div>
<Router>
<Route component={Navbar} />
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/api/:city/electronics" component={Electronics} />
<Route exact path="/api/:city/labour" component={Labour} />
<Route exact path="/api/posts/item/:id" component={ItemDetails} />
<Route exact path="/create/:city/:category" component={CreatePost} />
</Switch>
</Router>
</div>
);
thanks for your time.
I'm not sure I understand why your NavBar component is in it's own Route. Any components contained within the Router have access to the entire Router api, including Link - they do not need to be a Route to do so.
I would suggest wrapping all the Routes that include the NavBar with that component. The Routes will then be displayed as children of the Navbar component.
Here is a simplified example:
// App.js
return (
<div>
<Router>
<Switch>
<Route exact path="/" component={Home} />
<NavBar>
<Route exact path="/electronics" component={Electronics} />
<Route exact path="/labour" component={Labour} />
</NavBar>
</Switch>
</Router>
</div>
);
//NavBar.js
return (
<>
<div>
<Link to="/electronics">Electronics</Link>
<Link to="/labour">Labour</Link>
</div>
<div>{props.children}</div>
</>
);
codesandbox

Realize router transitions for solely one component

I have the following routes. I want only one route to be transitioned, namely /about. No other route shall have a routing transition. The component About shall be animated in from the bottom to top overlapping the previous component.
How can I ensure only the given route to be transitioned? Additionally the component CartHolder shall not be removed on route change - as you can see in the code below, I want it to be displayed on two different paths.
How can I realize such behavior? Is react-router-transition the way to go? - If so, how can I consider the conditions above?
const history = createBrowserHistory();
function App() {
return (
<div className="App">
<Router history={history}>
<ScrollToTop>
<Header/>
{projects ?
<div>
<Route path="/project/:name" component={() => <Project carts={projects}/>}/>
<Route path="/about" component={About}/>
<div className="hs">
<Route path={["/", "/project/:name"]} exact
component={() => <CartHolder carts={projects}/>}/>
</div>
<div className="hs-mobile">
<Route path="/" exact component={() => <CartHolderMobile carts={projects}/>}/>
</div>
</div>
: <div className="black-overlay"/>}
</ScrollToTop>
</Router>
</div>
)
}

How can I render a component in React to 2 different components?

I'm working on a ReactJS project. I have 3 components in the main page "Index component" as follows,
Nav-
Featured-
Footer
My Nav component has 2 links to 2 different components.
My Switch is as follows,
<Switch>
<Route path="/home" component={props => <Index {...props} />} />
<Route path="/register" component={Register} />
<Route path="/login" component={Login} />
<Route path="/cart" component={Cart} />
<Redirect from="/" to="home" />
</Switch>
I also have my Index component as follows,
<React.Fragment>
<Search />
<Nav history={history} />
{this.homePageComponents()}
<Route
path="/home/bedding"
component={props => (
<Bedding beddingProducts={this.beddingProducts()} {...props} />
)}
/>
<Route
path="/home/bath"
component={props => (
<Bath bathProducts={this.bathProducts()} {...props} />
)}
/>
<Route path="/home/search" component={Search} />
</React.Fragment>
I'm trying to render the Nav component to both Bath and Bedding products but whenever I import it and use it there it gives me an Error saying this.props.history.replace is undefined.
This is the project's link.
https://github.com/MaxOffline/beetle
I think you can solve your issue with using "withRouter" wrapper component which react-router provide to wrap your Nav component.
https://reacttraining.com/react-router/web/api/withRouter
If you console.log(this.props) in the render method of nav.jsx you'll see that it contains a history object but that there is no replace method on that object.
You may be looking for the this.props.history.push method?
I found a very simple way to fix the problem.
All I needed to do is render components to the homepage conditionally so I simply added the following helper method to my "Index" Component.
homePageComponents = () => {
const featuredProducts = this.state.products.filter(
product => product.featured === true
);
if (this.props.history.location.pathname === "/home") {
return <Featured featuredProducts={featuredProducts} />;
}
};

Categories

Resources