I have a BrowserRouter which renders different components based on the Route. Most, of these components have similar markup.
So, I created a Wrapper component which will recieve props, and render its {children} if provided. This Wrapper is called in Route's.
import React, {Component} from 'react'
import Context from '../../provider'
import {
BrowserRouter,
Route,
Redirect,
Switch,
} from "react-router-dom"
import {
Container,
Row,
Col,
} from 'reactstrap'
import Profile from './ContentComponent/Profile'
import Subreddit from './ContentComponent/Subreddit'
import PostExpanded from './ContentComponent/PostExpanded'
import InfoComponent from './InfoComponent'
import SwitchTab from './ContentComponent/Subreddit/SwitchTab'
import NewPost from './ContentComponent/assets/NewPost'
import './style.css'
class Wrapper extends React.Component {
componentDidMount() {
this.props.setActiveTab(this.props.activeTab);
}
render() {
{console.log('Wrapper props: ', this.props)}
return (
<Row>
<Col md='8' id='content-block'>
<SwitchTab />
{this.props.children}
</Col>
<Col md='4' id='info-block'>
<InfoComponent info={this.props.info} {...this.props}/>
</Col>
</Row>
)
}
}
export default class BodyComponent extends Component {
render() {
return (
<BrowserRouter>
<Context.Consumer>
{context => {
return (
<Container>
<Switch>
<Route
exact
path='/'
render={() =>
<Redirect to='r/home/' />
}
/>
<Route
exact
path='/r/home/'
render={() =>
<Wrapper
setActiveTab={context.toggleTab}
activeTab={'1'}
info='home'
/>
}
/>
<Route
exact
path='/r/popular/'
render={() =>
<Wrapper
setActiveTab={context.toggleTab}
activeTab={'2'}
info='popular'
/>
}
/>
<Route
exact
path='/r/all/'
render={() =>
<Wrapper
setActiveTab={context.toggleTab}
activeTab={'3'}
info='all'
/>
}
/>
<Route
exact
path='/u/:username/'
render={(props) => {
return (
<Wrapper
setActiveTab={context.toggleTab}
activeTab={'4'}
info='user'
user={props.match.params.username}
>
<Profile username={props.match.params.username} />
</Wrapper>
)
}}
/>
<Route
exact
path = '/r/:subreddit/new/'
render={(props) => {
return (
<Wrapper
setActiveTab={context.toggleTab}
activeTab={'4'}
info='subreddit'
subreddit={props.match.params.subreddit}
>
<NewPost />
</Wrapper>
)
}}
/>
<Route
exact
path = '/r/:subreddit/post/:postid/'
render={(props) => {
return (
<Wrapper
setActiveTab={context.toggleTab}
activeTab={'4'}
info='subreddit'
subreddit={props.match.params.subreddit}
>
<PostExpanded
subreddit={props.match.params.subreddit}
postid={props.match.params.postid}
/>
</Wrapper>
)
}}
/>
<Route
exact
path='/r/:subreddit/'
render={(props) => {
return (
<Wrapper
setActiveTab={context.toggleTab}
activeTab={'4'}
info='subreddit'
subreddit={props.match.params.subreddit}
>
<Subreddit subreddit={props.match.params.subreddit} />
</Wrapper>
)
}}
/>
<Route
exact
path = '/new/'
render={(props) => {
return (
<Wrapper
setActiveTab={context.toggleTab}
activeTab={'4'}
info='new'
>
<NewPost />
</Wrapper>
)
}}
/>
</Switch>
</Container>
)
}}
</Context.Consumer>
</BrowserRouter>
)
}
}
I am facing multiple problems here and I think they can all be fixed at once, I don't know how?
The Wrapper props are not getting changed when I am changing the URL
using props.history.push:
<NavItem>
<NavLink
className={classnames({ active: context.activeTab === '1' })}
onClick={() =>{
context.toggleTab('1');
this.props.history.push('/r/home/')
}}
>
Home
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={classnames({ active: context.activeTab === '2' })}
onClick={() => {
context.toggleTab('2');
this.props.history.push('/r/popular/')
}}
>
Popular
</NavLink>
</NavItem>
I think I got the problem while going through my code. So, the problem started when I wanted to use props.history.push in my HeaderComponent. I was getting error so I wrapped it with BrowserRouter and exported withRouter() which enabled me to use props.history.push
So, unknowingly I have created 2 BrowserRouter's:
<React.Fragment>
<BrowserRouter>
<NavbarComponent />
<TabComponent />
</BrowserRouter>
<BrowserRouter>
<Switch>
<Route ... />
<Route ... />
</Switch>
</BroserRouter>
</React.Fragment>
So, I was changing URLs in TabComponent and expecting that to change my Route's and modify content.
Having a global BrowserRouter (don't think that global's the right word but) solved the problem.
<React.Fragment>
<BrowserRouter>
<NavbarComponent />
<TabComponent />
<Switch>
<Route ... />
<Route ... />
</Switch>
</BroserRouter>
</React.Fragment>
Related
[![enter image description here][1]][1]I'm using all latest version and styled components, This is my App.js file
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
function App() {
return (
<div className="App">
<GlobalStyle/>
<Router>
<Navbar />
<Routes>
<Route path="/" exact element={<Hero />} />
<Route path="/projects" exact element={<Projects />} />
<Route path="/about" exact element={<About />} />
</Routes>
</Router>
</div>
);
}
Navbar.js
{menuData.map((item, index) => {
const border = index === menuData.length-1 ? '1px solid #fff':'none';
const padding = index === menuData.length-1 ? '0 24px':'0';
return (
<NavItem key={index}>
<NavLinks to={item.link} style={{border, padding}}>
{item.title}
</NavLinks>
</NavItem>
)
})}
I've read some other articles about people facing the same issue but still haven't found anything that works for me. What has me confused is that the Login.js code works when I run it directly without navigation. Likewise, if I change the Login.js code and merely render an H1 tag, it works even with navigation.
AppRouter.js
class AppRouter extends Component {
render() {
return (
<BrowserRouter>
<div>
<Navigation />
<Routes>
<Route exact path="/" element={<Home/>}/>
<Route exact path="/login" element={<Login/>}/>
<Route component={Error}/>
</Routes>
</div>
</BrowserRouter>
);
}
}
export default AppRouter;
Navigation.js
import React from 'react';
import { NavLink } from 'react-router-dom';
const Navigation = () => {
return (
<div>
<NavLink to="/">Home</NavLink>
<NavLink to="/login">Login</NavLink>
</div>
);
}
export default Navigation;
Login.js
export default function Login(){
{
return (
<>
<Header/>
<Flex
minH={'100vh'}
align={'center'}
justify={'center'}
bg={useColorModeValue('gray.50', 'gray.800')}>
<Stack spacing={8} mx={'auto'} maxW={'lg'} py={12} px={8}>
<Stack align={'center'}>
<Heading fontSize={'4xl'}>Sign in to your account</Heading>
<Text fontSize={'lg'} color={'gray.600'}>
Welcome!<Link color={'blue.400'}>features</Link>
</Text>
</Stack>
<Box
rounded={'lg'}
bg={useColorModeValue('white', 'gray.700')}
boxShadow={'lg'}
p={8}>
<Stack spacing={4}>
<FormControl id="email">
<FormLabel>Email address</FormLabel>
<Input type="email" />
</FormControl>
<FormControl id="password">
<FormLabel>Password</FormLabel>
<Input type="password" />
</FormControl>
<Stack spacing={10}>
<Stack
direction={{ base: 'column', sm: 'row' }}
align={'start'}
justify={'space-between'}>
<Checkbox>Remember me</Checkbox>
<Link color={'blue.400'}>Forgot password?</Link>
</Stack>
<Button
bg={'blue.400'}
color={'white'}
_hover={{
bg: 'blue.500',
}}>
Sign in
</Button>
</Stack>
</Stack>
</Box>
</Stack>
</Flex>
</>
)
}
}
App.js
function App() {
return (
<AppRouter/>
);
}
Try this!
class AppRouter extends Component {
render() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login/>}/>
<Route path="/Error" element={<div>Error Message</div>} />
<Route path="*" element={<Navigate to="/Error" />} />
</Routes>
</BrowserRouter>
);
}
}
export default AppRouter;
Codesandbox Example
I have an issue with navigating pages via Route.
I used semantic ui react components to handle with the issue.
When I link any item in navbar, the url is changed like http://localhost:3000/ to http://localhost:3000/list but the relevant component page cannot open.
How can I fix it?
Here is my index.js code shown below.
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter> ,
document.getElementById('root')
);
Here is my App.js code shown below.
function App() {
return (
<div className="App">
<Container style={{"marginTop": "20px"}}>
<Header />
<Route path="/">
<AccordionComponent items={items} />
</Route>
<Route path="/list">
<Search />
</Route>
<Route path="/dropdown">
<DropdownForm options={options}/>;
</Route>
<Route path="/translate">
<Translate />
</Route>
</Container>
</div>
);
}
export default App;
Here is my Route Component shown below.
const Route = ({ path, children }) => {
return window.location.pathname === path ? children : null;
};
export default Route;
Here is my Header Component shown below.
import React, { useState } from 'react';
import { Menu } from 'semantic-ui-react'
import { Link } from "react-router-dom";
const Header = () => {
const [activeItem, setActiveItem] = useState('Accordion');
const handleItemClick = (name) => setActiveItem(name)
return (
<Menu secondary pointing>
<Menu.Item
as={Link} to="/"
name='Accordion'
active={activeItem === 'Accordion'}
onClick={() => handleItemClick('Accordion')}
/>
<Menu.Item
as={Link} to="/list"
name='Search'
active={activeItem === 'Search'}
onClick={() => handleItemClick('Search')}
/>
<Menu.Item
as={Link} to="/dropdown"
name='Dropdown'
active={activeItem === 'Dropdown'}
onClick={() => handleItemClick('Dropdown')}
/>
<Menu.Item
as={Link} to="/translate"
name='Translate'
active={activeItem === 'Translate'}
onClick={() => handleItemClick('Translate')}
/>
</Menu>
);
};
export default Header;
Wrap de routes with Switch as this example:
function App() {
return (
<div className="App">
<Container style={{"marginTop": "20px"}}>
<Header />
<Switch>
<Route path="/">
<AccordionComponent items={items} />
</Route>
<Route path="/list">
<Search />
</Route>
<Route path="/dropdown">
<DropdownForm options={options}/>;
</Route>
<Route path="/translate">
<Translate />
</Route>
</Switch>
</Container>
</div>
);
}
export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Docs: https://reactrouter.com/web/api/Switch
I'm using react-router in my app for routing, where all of my routes work apart from the last one which is being used for modal purposes, however it's not loading the component.
function App() {
return (
<BrowserRouter basename="/veochorusapp">
<ModalSwitch />
</BrowserRouter>
);
}
export default App;
function ModalSwitch() {
let location = useLocation();
let background = location.state && location.state.background;
return (
<div className="App">
<Switch location={background || location}>
<Route exact path="/" component={Home} />
<Route path="/applications" component={Applications} />
<Route path="/waterType" component={WaterType} />
<Route path="/feedType" component={FeedType} />
<Route path="/products/:productName" component={Product} />
<Route path="/products" component={Products} />
<Route path="/interactive" component={ProductView} />
</Switch>
{background && <Route path="/feature/:id" children={<Modal />} />}
{background && <Route path="/accessory/:id" children={<AccessoryModal />} />}
</div>
);
}
The route that is not loading the component is the following:
{background && <Route path="/accessory/:id" children={<AccessoryModal />} />}
The code that links to this route is as below. I have used the same process on a previous component which works perfectly.
function AccessoryBtns() {
let location = useLocation();
return (
<ul className="accessoryBtns">
{AccessoriesData.map((i, index) => (
<li key={index}>
<Link
key={i.id}
className="btn"
to={{
pathname: `/accessory/${i.id}`,
state: { background: location }
}}
>
{i.name}
</Link>
</li>
))}
</ul>
)
}
export function AccessoryModal() {
let history = useHistory();
let { id } = useParams();
let accessory = AccessoriesData[parseInt(id, 10)];
if (!accessory) return null;
let back = e => {
e.stopPropagation();
history.goBack();
};
return (
<div className="modal">
<div className="row">
<div className="col-md-6 text-right">
Image
</div>
<div className="col-md-6">
<h2>{accessory.name}</h2>
<p>{accessory.description}</p>
</div>
</div>
<div className="backBtn" onClick={back}><FontAwesomeIcon icon={faArrowLeft} /></div>
</div>
);
}
First, it looks they are not inside <Switch>. Second, according to router docs, children must be a function
{background && <Route path="/feature/:id" children={() => <Modal />} />}
But since you pass no props to Modal you can simply use
{background && <Route path="/feature/:id">
<Modal />
</Route>}
or
{background && <Route path="/feature/:id" component={Modal} />}
I have problem with nested routing.
On the normal site I have other urls than on the / admin page and i have different design and html.
I prepared this sample routing but after the page refreshes, the page gets white without any error.
Can I ask for a consultation what did I do wrong?
APP COMPONENT
class App extends Component {
render() {
return (
<BrowserRouter>
<div className="container">
<Route exact path="/" render={(props) => (
<Page {...props} data={data} />
)} />
<Route exact path="/admin" render={(props) => (
<Admin {...props} data={data} />
)} />
</div>
</BrowserRouter>
);
}
}
PAGE COMPONENT
class Page extends React.Component {
render() {
return (
<BrowserRouter>
<div>
<Header />
<Route exact path="/" render={(props) => (
<Home {...props} videosJson={this.props.data} />
)} />
<Route path="/about" component={ About } />
<Route exact path="/video" render={(props) => (
<VideoGallery {...props} videosJson={this.props.data} />
)} />
<Route path="/video/:id" render={(props) => (
<VideoPage {...props} videosJson={this.props.data} />
)} />
<Route exact path="/photo" render={(props) => (
<PhotoGallery {...props} videosJson={this.props.data} />
)} />
<Route path="/photo/:id" render={(props) => (
<PhotoPage {...props} videosJson={this.props.data} />
)} />
<Route path="/contact" component={ Contact } />
<Footer />
</div>
</BrowserRouter>
)
}
}
ADMIN COMPONENT
class Admin extends React.Component {
render() {
return (
<BrowserRouter>
<div>
<Route exact path="/admin" render={(props) => (
<Dashboard {...props} />
)} />
</div>
</BrowserRouter>
)
}
}
Your React application which uses React-Router should only have one instance of a Router defined, as stated in the documentation:
The common low-level interface for all router components. Typically
apps will use one of the high-level routers instead
The error you are getting is because you are defining additional routers (in your case there are multiple instances of BrowserRouter) in your Page and Admin components.
Also some of your Routes are ambiguous e.g.
<Route exact path="/" render={(props) => (
<Page {...props} data={data} />
)} />
and:
<Route exact path="/" render={(props) => (
<Home {...props} videosJson={this.props.data} />
)} />
One Route says that root ('/') should navigate to the Page component, the other says that root should navigate to the Home component, hence there is a conflict. Make sure the routes are unique.
I change my approach to this situation but dont work. Url /admin load Header and Footer component although he should not and component Dashboard not load.
Any sugestion?
<BrowserRouter>
<div className="container">
<Page>
<Header />
<Route exact path="/" render={(props) => (
<Home {...props} videosJson={data} />
)} />
<Route path="/about" component={ About } />
<Route exact path="/video" render={(props) => (
<VideoGallery {...props} videosJson={data} />
)} />
<Route path="/video/:id" render={(props) => (
<VideoPage {...props} videosJson={data} />
)} />
<Route exact path="/photo" render={(props) => (
<PhotoGallery {...props} videosJson={data} />
)} />
<Route path="/photo/:id" render={(props) => (
<PhotoPage {...props} videosJson={data} />
)} />
<Route path="/contact" component={ Contact } />
<Footer />
</Page>
<Admin>
<Route exact path="/admin" render={(props) => (
<Dashboard />
)} />
</Admin>
</div>
</BrowserRouter>
Admin Component:
class Admin extends React.Component {
render() {
console.log("ADMIN:", this.props);
return (
<div className="row">
<h1>ADMIN</h1>
{this.props.children}
</div>
)
}
}
Page Component:
class Page extends React.Component {
render() {
console.log("PAGE:", this.props);
return (
<div>
{this.props.children}
</div>
)
}
}