How can I put a class as a element parameter in react router? - react-router

function App() {
return (
<Router>
<Routes>
<Route path="/nav" element={Nav()}/>
<Route path="/page1" element={Page1()}/>
<Route path="/page2" element={Page2()}/>
<Route path="/map" element={Mapp()}/>
</Routes>
</Router>
);
}
The above is my function, which I render. nav, page1, and page2 all work fine since I wrote them as functions. However, the "Mapp" is a class. How can I make this work?

In react-router-dom v6 the Route component's element prop takes a ReactElement, i.e JSX. I suspect the Nav, Page, and Page2 function components work because they take no props and return JSX. If these are actually React components they should be passed as JSX. Same goes for class-based components, pass them as JSX.
function App() {
return (
<Router>
<Routes>
<Route path="/nav" element={<Nav />}/>
<Route path="/page1" element={<Page1 />}/>
<Route path="/page2" element={<Page2 />}/>
<Route path="/map" element={<Mapp />}/>
</Routes>
</Router>
);
}

Related

Main content wrapper best practices

I want to create a main content wrapper so I don't have to add classes to each component separately, I use tailwindCSS. It is just a couple of classes, mainly to give the content a margin, a max width and keep it centered.
I put a couple of divs encompasing the routes, but I don't know if this is considered a good practise.
Here is my App.jsx
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
// pages
import Navbar from './Navbar';
import Home from './Home';
import About from './About';
import Contact from './Contact';
function App() {
return (
<Router>
<div className='font-mono'>
<Navbar />
<div className='flex justify-center'>
<div className='m-10 lg:max-w-4xl'>
<Routes>
<Route path='/' element={<Home />}/>
<Route path='/about' element={<About />} />
<Route path='/contact' element={<Contact />} />
</Routes>
</div>
</div>
</div>
</Router>
);
}
export default App;
Yeah I imagine this is pretty common. I typically abstract it in a separate component.
/components/container/Container.js
export default function Container({ children }) {
return (
<div className='flex justify-center'>
<div className='m-10 lg:max-w-4xl'>{children}</div>
</div>
);
}
You can then import it and use it wherever you'd like. In the case of wrapping your routes I suppose you won't be reusing it elsewhere, but it does still remove some visual noise and keep things a little organized.
/App.js
import Container from './components/container/Container';
function App() {
return (
<Container>
<Router>
<Routes>
<Route path='/' element={<Home />}/>
<Route path='/about' element={<About />} />
<Route path='/contact' element={<Contact />} />
</Routes>
</Router>
</Container>
);
}

How to pass props from <Route /> to a component

Even after using the render option in the component, the component inside render is not receiving an new prop.
Have checked if other default props are accessible and they are.
Layout.js:
<Switch>
<Route
exact
path="*"
render={(props) => <HomeComponent {...props} loc={constants["404"]} />}
/>
</Switch>
HomeComponent.js:
componentDidMount() {
console.log(this.props.loc) // Output is shown as undefined
}
I expect the prop loc to be filled with the data for 404 page, but receiving undefined

React Router - are nested <Switch> components an anti-pattern?

From React Router's docs:
All children of a <Switch> should be <Route> or <Redirect> elements. Only the first child to match the current location will be rendered.
Nonetheless, nested <Switch> statements are allowed. I use the pattern to break up large numbers of <Routes>:
<Switch>
<Route path="/foo" component={FooRouter} />
<Route path="/bar" component={BarRouter} />
<Route path="/baz" component={BazRouter} />
</Switch>
...
const FooRouter = () => (
<Switch>
<Route exact path="/foo/:id" component={ViewFoo} />
<Route exact path="/foo/new" component={NewFoo} />
</Switch>
)
const BarRouter = () => (
<Switch>
<Route exact path="/bar/new" component={NewBar} />
</Switch>
)
....
Curious if there is a better method for breaking up large numbers of routes and if nested <Switch> statements should be avoided?
as you solve it just fine when you have a lot of nested route yo can speared them across the app and make a dynamic routes
but soon react-router-dom v6 will be release with a huge upgrade one of them is useRoutes
that let you configure your routes like this:
let element = useRoutes([
// A route object has the same properties as a <Route>
// element. The `children` is just an array of child routes.
{ path: '/', element: <Home /> },
{
path: 'users',
element: <Users />,
children: [
{ path: '/', element: <UsersIndex /> },
{ path: ':id', element: <UserProfile /> },
{ path: 'me', element: <OwnUserProfile /> },
]
}
]);
introduction to react-router-dom v6 they have some cool new feature that worth to watch for
one of them is the replace of with witch help you a lot with nested routes and fun thing you don't gonna need to use the exact anymore
<Routes>
<Route path="/" element={<UsersIndex />} />
<Route path=":id" element={<UserProfile />} />
<Route path="me" element={<OwnUserProfile />} />
</Routes>
this is how it gonna look with the new feature
A note on nested conditional routes: Switch must only have Route children. If you declare Switch inside Switch, every route after Switch won't be used, i.e.
<Switch>
<Route path="/1" />
<Switch> ... </Switch>
<Route path="/2" /> // this one won't work!
</Switch>
So don't do this, stick to declaring one route per condition or render routes as an array under common condition:
<Switch>
{condition && <Route path="/1" >}
{condition && <Route path="/2">}
{/* or */}
{anotherCondition && [
// notice `key`. React will warn you about rendering a list without key prop
<Route key="3" path="/3">,
<Route key="4" path="/4">,
]}
</Switch>
This is true for react-router-dom v5, not sure about 6.

react router query parameters changed component will unmount

When query parameters changed, the same component will unmount and then mount.for example:
I have a url like /admin and also have a component called Admin. In Admin, there are some inputs for searching. I add a query parameters after /admin like /admin?userId=123.The componet's componentDidMount will excute again. Is there any way to prevent this?
and setting likes this
export default function (history, app) {
return (
<Switch>
<Route exact path='/admin/settings/user' component={getComponent(User,app,userModel)} />
<Route path='/admin/settings/user/:id' component={getComponent(UserEdit,app,userModel)} />
<Route path='/admin/settings/role' component={getComponent(Role,app,roleModel)} />
<Route path='/admin/settings/menu' component=
</Switch>
)
}
getComponent is a layload component.
#Alex Brazh I used v4 and the router likes this;
<Router>
<Switch>
<Route exact path='/' component={getComponent(Login,app,loginModel)}/>
<Route path='/admin' render={ props => (
<Layout>
{ settings(history, app) }
</Layout>
)}/>
<Route path='/finance' render={ props => (
<Layout>
{ finance(history, app) }
</Layout>
)}/>
</Switch>
</Router>
You can use the URL interface to set query string values without unmount and mount your components:
const queryStringValue = 'bar'
const url = new URL(window.location.toString());
url.searchParams.set('foo', queryStringValue);
window.history.replaceState(null, '', url.toString());
Also, this solution won't add a new item in browser navigation stack

React-Router v4: Cannot read property 'route' of undefined

I want to redirect when I hit a button, so I used the withRouter to get the access to the history props.
But I get the error:
Uncaught TypeError: Cannot read property 'route' of undefined
at Route.computeMatch (react-router.js:1160)
error when I wrap my component with the withRouter HOC.
If I remove withRouter function, it just works.
My code looks like the following:
class App extends Component {
// ...some unrelated functions
handleTitleTouchTap = e => {
e.preventDefault()
const { history } = this.props
history.push('/')
}
render() {
//...other components
<Router>
<div>
<Switch>
<Route exact={true} path="/" component={Home} />
<Route path="/search" component={Search}/>
<Route path="/gamelist/:listId" component={GameListDetail}/>
<Route path="/game/:gameId" component={GameDetail}/>
<Route path="/manageuser" component={ManageUser} />
<Route path="/addgamelist" component={AddGameList} />
<Route path="/addgame" component={AddGame} />
<Route path="/test" component={Test} />
<Route component={NoMatch} />
</Switch>
<LoginForm isLoginFormOpen={isLoginFormOpen} closeLoginForm={closeLoginForm} handleLogin={handleLogin}/>
<RegisterForm isRegisterFormOpen={isRegisterFormOpen} closeRegisterForm={closeRegisterForm} register={register}/>
</div>
</Router>
)
}
const mapStateToProps = state => ({
//some props
})
const mapDispatchToProps = dispatch => ({
//some functions
})
const Container = connect(mapStateToProps, mapDispatchToProps)(App)
export default withRouter(Container)
I've got the same issue and I solved it enclosing the wrapped component in a Router component (namely BrowserRouter).
In your example, it would become:
// assuming this file is Container.js
export default withRouter(Container)
// index.js
import Container from './Container'
render(
<BrowserRouter>
<Container/>
</BrowserRouter>,
document.getElementById('root')
)
Working example from the docs here: https://codepen.io/pietro909/pen/RVWmwZ
I also opened an issue on the repo because the example from the docs is not clear enough in my opinion https://github.com/ReactTraining/react-router/issues/4994.