React router v5 renders only index page on all routes - javascript

ISSUE
any route written manually in URL or triggered by button renders only index page (route name is changing, content of the page is always content of index)
App.js (handling all the main functions from the app)
// Exporting my router and passing all states/functions from App
<div className="App">
<Routes
........
........
........
onRemove={onRemove}
setCartOpen={setCartOpen}
cartOpen={cartOpen}
.......
.......
/>
</div>
Router (example of how my routes work)
export default function Routes({....., ....., onRemove,setCartOpen,cartOpen,..... })
<Router>
<Header
countCartItems={cartItems.length}
cartItems={cartItems}
onAdd={onAdd}
onRemove={onRemove}
setCartOpen={setCartOpen}
/>
<Suspense fallback={<Spinner />}>
<Switch>
<Route
path={baseRouteUrl + '/'}
component={(props) => (
<IndexPage
{...props}
.......
.......
onRemove={onRemove}
setCartOpen={setCartOpen}
cartOpen={cartOpen}
.......
.......
/>
)}>
</Route>
<Route
path={baseRouteUrl + '/about'}
component={About}
></Route>
<Route
path={baseRouteUrl + ''}
component={NotFound}
></Route>
.......
And Index.js
ReactDOM.render(
<React.StrictMode>
<QueryClientProvider client={client}>
<ChakraProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</ChakraProvider>
</QueryClientProvider>
</React.StrictMode>,
document.getElementById('root')
);
NOTE
Before trying to pass props in the routes the router was working just fine
The reason I decided to structure my app this way is because I need my Header to be exported on all pages with all the functionalities.
Is there anything I should do in a different way? I know it might be a little messy, so I'll be grateful to learn how to handle it cleaner and most important, how to make it work so all routes will render the correct pages.

Just like in most languages, a switch will match and runs the first thing it runs into.
In this case, it's matching your default path and rendering that one first.
One of the way to deal with this is to set exact to true
<Route exact path={...}... />

react router does partial matching so '/' is partial from '/about' when using exact it will disable the partial matching and will get the correct path
<Route
exact
path={baseRouteUrl + '/'}
component={(props) => (
<IndexPage
{...props}
.......
.......
onRemove={onRemove}
setCartOpen={setCartOpen}
cartOpen={cartOpen}
.......
.......
/>
)}>
</Route>

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

I want to test if the routes exist

I want to test if the routes exist in the component. App should render the specific component on the specific path.
App.js
return (
<Router>
<Header jsTrendingTopics={this.props.data} />
<Switch>
<Route exact path="/" render={() => <Dashboard jsTrendingTopics={this.props.data} />} />
<Route exact
path="/language/:languagename"
render={(props) => this.handleTopic(props)
}
/>
</Switch>
</Router>
)
}
I expect the test to return the component that should render on that specific route.
I think MemoryRouter with initialEntries={['/]} is what you are looking for.
import Dashboard from ...
jest.mock('path to Dashboard component');
Dashboard.mockImplementationOnce(() => <div>Dashboard</div>);
render(
<MemoryRouter initialEntries={['/']}>
<App/>
</MemoryRouter>
);
expect(screen.getByText("Dashboard").toBeInTheDocument()
You can read this document for more examples
https://javascript.plainenglish.io/testing-react-router-with-jest-bc13d367bad

Nested react routes with different page layouts

I'm having two issues with my code:
Number One:
My whole application seems to work fine, I can access all my routes when I first load my web app. My app consists of a Landing Page with its navbar that has a login button that takes me straight to my application's home page (haven't added authentication yet). This home page has a navbar that is different from the one on the landing page. The navbar items are (home, about and LandingPage). My App.js has routes to the landing page component, the home page which is the Gitapp component, and a PageNotFound component. The Gitapp component contains routes to the about page and other components. If i happen to reload the page while i'm on one of the routes on App.js (first-level routes) it reloads fine. However, if I'm on the routes (second-level routes) that exist on my Gitapp component, like the route for the about page and i reload the page, I get the PageNotFound Component.
Number Two:
My second Navbar has a logout button that should take me back to the landing page. For some Reason I can't get it to work because If I add the route to the landing page in my Gitapp component, React will try to display the landing page underneath the Home page.
This is App.js:
const App = () => {
return (
<Fragment>
<Router>
<Switch>
<Route exact path='/' component={LandingPage} />
<Route exact path='/gitapp' component={GitApp} />
<Route component={PageNotFound} />
</Switch>
</Router>
</Fragment>
);
};
This is LandingPage.js:
const LandingPage = () => {
return (
<div>
<NavbarLanding />
<SideNavBar />
<LandingSection1 />
<LandingSection2 />
<LandingSection3 />
<Route exact path='/gitapp' component={GitApp} />
</div>
);
};
This is Gitapp.js:
const GitApp = ({ match }) => {
return (
<GithubState>
<Router>
<div style={containerStyling}>
<Navbar />
<Switch>
<Route exact path={match.url} component={Home} />
<Route
exact
path={`${match.url}/user/:login`}
component={UserProfile}
/>
<Route exact path={`${match.url}/about`} component={About} />
</Switch>
<Footer />
</div>
</Router>
</GithubState>
);
};
const containerStyling = {
minHeight: '100vh',
overflow: 'hidden',
display: 'block',
position: 'relative',
paddingBottom: '70px'
};
I have resolved both issues! I had to get rid of the word exact in the Gitapp route defined in App.js.
So instead of:
const App = () => {
return (
<Fragment>
<Router>
<Switch>
<Route exact path='/' component={LandingPage} />
<Route exact path='/gitapp' component={GitApp} /> {/* Wrong! */}
<Route component={PageNotFound} />
</Switch>
</Router>
</Fragment>
);
};
It should be:
const App = () => {
return (
<Fragment>
<Router>
<Switch>
<Route exact path='/' component={LandingPage} />
<Route path='/gitapp' component={GitApp} /> {/* Correct! */}
<Route component={PageNotFound} />
</Switch>
</Router>
</Fragment>
);
};
Don't know exactly why but I can reload the second level components instead of receiving the NotFound Component. Would appreciate it if someone could explain why the word exact made a different here.
As for my second issue, I just used redirect with conditional rendering. So my context api will update my global 'logout' state and pass it down to the component which then would be waiting for it ('logout' state) to become true and will then redirect me to the landing page.

Categories

Resources