React-router: change in url but not in components displayed - javascript

I have a problem with react-router. This is my /home component, the first page you see after logging in:
return(
<>
<CustomNavbar />
<Container fluid>
{ loading ? (
<div>Loading...</div>
) : (
<Router>
<ProtectedRoute
path='/home/mem'
render={(props) => <MResult
{...props}
data={data}
users={users} />
}
/>
<ProtectedRoute
path='/home/reading'
render={(props) => <RResult
{...props}
data={readingData}
users={users} />
}
/>
</Router>
)}
</Container>
</>
)
I have a table and I need to display different data based on the second part of the url (I'm using my own component ProtectedRoute to check for authentication, but the behaviour is the same we the usual Route component).
In the CustomNavbar component I have two links:
return(
<Navbar bg="primary" variant="dark">
<Navbar.Brand className='cursor-default'>
<span style={{marginLeft: "10px"}}></span>
</Navbar.Brand>
<Navbar.Toggle />
<Navbar.Collapse>
{ props.authenticated && (
<>
<Button>
<Link to="/home/reading">Reading</Link>
</Button>
<Button>
<Link to="/home/mem">MemResult</Link>
</Button>
</>
)}
</Navbar.Collapse>
<Navbar.Collapse className='justify-content-end'>
{ props.authenticated && (
<Button onClick={logout}>logout</Button>
)}
</Navbar.Collapse>
</Navbar>
)
The problem is that if I click on the links in the navbar I can see the url changing accordingly, but the new component is not being loaded, I still see the previous one. If I hit the browser's refresh button, then the correct component is loaded, but once that happened, again, clicking on the links won't change a thing but the url.
How can I fix this?

The CustomNavbar component is outside of the Router provider which is why Links aren't able to communicate to the Route component.
The solution is to render Router component at the top level and render CustomNavbar as a default route
return(
<Router>
<Route component={CustomNavbar} />
<Container fluid>
{ loading ? (
<div>Loading...</div>
) : (
<ProtectedRoute
path='/home/mem'
render={(props) => <MResult
{...props}
data={data}
users={users} />
}
/>
<ProtectedRoute
path='/home/reading'
render={(props) => <RResult
{...props}
data={readingData}
users={users} />
}
/>
</Router>
)}
</Container>
</Router>
)

Related

Big React component with conditional rendering or smaller separate components?

I have a specific question about coding practices for React.js.
I have two pages in my React application that render UI cards based on data returned from the backend. They are almost identical, except one has a UI for filter options and another has a search bar.
What would be the better way of structuring these two different pages and why?
----------------
OPTION A (one big component with conditional rendering)
App.jsx
<Router>
<Routes>
<Route path="/page1" element={<Page showFilter=true showSearchBar=false />} />
<Route path="/page2" element={<Page showFilter=false showSearchBar=true />} />
</Routes>
</Router>
Page.jsx
function Page(props) {
return (
<>
<Header />
{props.showSearchBar && (<SearchBar />)}
{props.showFilter && (<Filter />)}
<ItemsList />
<Footer />
</>
)
}
----------------
OPTION B (two separate components with sub-component reuse)
App.jsx
<Router>
<Routes>
<Route path="/page1" element={<Page1 />} />
<Route path="/page2" element={<Page2 />} />
</Routes>
</Router>
Page1.jsx
function Page1(props) {
return (
<>
<Header />
<Filter />
<ItemsList />
<Footer />
</>
)
}
Page2.jsx
function Page2(props) {
return (
<>
<Header />
<SearchBar />
<ItemsList />
<Footer />
</>
)
}
----------------
Of course the above list of options isn't exhaustive. If there are any other ways to solve this that might be more suitable for my task, I'd be happy to hear it.
Thanks!
Option C React Composition
App.jsx
<Router>
<Routes>
<Route path="/page1" element={<Page><Filter /></Page>} />
<Route path="/page2" element={<Page><SearchBar /></Page>} />
</Routes>
</Router>
Page.jsx
function Page(props) {
return (
<>
<Header />
{props.children}
<ItemsList />
<Footer />
</>
)
}
I think option A seems fine also, at least until the UI start to differ significantly then you might have to go for option B.

React Router Shows Blank Page When Using <Link>

After updated a bunch of our dependencies we are now are forced to use anchor tags which causes redirects, before we used but now everytime we click on a it goes to the proper url ie /screens but is just a blank page and never presents the components, below is our parent index.tsx file:
ReactDOM.render(
<Provider {...stores}>
{/* Changed routes to children */}
{/* <Router children={routes} history={createHistory} /> */}
<Router history={createHistory}>
<div>
<Switch>
<Route path='/oauth2callback' componentDidMount={console.log()}>
{() => {
if(window.location.href.includes('/oauth2callback'))
{
oauth2callback(window.location.hash)
}
}}
</Route>
<Route path='/testing' component={Get} />
<Route path='/'>
{/* Function that determines if the user is logged in and can allow the authenticated passage or not. If no, it will not render authenticated pages*/}
{() => {
if(auth.loggedIn())
{
console.log("is logged in")
return(
<div>
<Route component={App} history={createHistory}/>
<div className="row" id="wrapper">
<Switch>
{/* <Route path='/screens' component={Screens}/> */}
<Route path='/screens' component={Screens}/>
<Route path='/playlists' component={Playlists}/>
<Route path='/content' component={Content}/>
<Route path='/help' component={HelpNav}/>
<Route component={NotFound}/>
</Switch>
</div>
</div>
)
}
else
{
console.log("is not logged in")
return(
<Route path='/' component={HomeNav}/>
)
}
}}
</Route>
<Route path='401' component={Get} />
</Switch>
</div>
</Router>
</Provider>,
and here is the app.tsx with the navbar:
<Nav className="mb-nav">
{/* <IndexLinkContainer to="/screens"> */}
<Nav.Item style={{width: widthConstants[0]}}>
<Link to='/screens' style={{color: this.state.navIndex === 0 ? "white" : "bbddba"}}>
Screens
</Link>
</Nav.Item>
{/* </IndexLinkContainer> */}
{/* <LinkContainer to="/playlists"> */}
<Nav.Item style={{width: widthConstants[1]}}>
<a href="/playlists" style={{color: this.state.navIndex === 1 ? "white" : "bbddba"}}>
Playlists
</a>
</Nav.Item>
{/* </LinkContainer> */}
{/* <LinkContainer to="/content"> */}
<Nav.Item style={{width: widthConstants[2]}}>
<a href="/content" style={{color: this.state.navIndex === 2 ? "white" : "bbddba"}}>
Content
</a>
</Nav.Item>
{/* </LinkContainer> */}
{/* <LinkContainer to="/help"> */}
<Nav.Item style={{width: widthConstants[3]}}>
<a href="/help" style={{color: this.state.navIndex === 3 ? "white" : "bbddba"}}>
Help
</a>
</Nav.Item>
{/* </LinkContainer> */}
{this.shouldRenderTouch && <TouchButton/>}
</Nav>
</Navbar.Collapse>
</div>
</Navbar>
any idea what is different in the new react router update to cause this bug to exist?
The fix was much simpler than I thought, the new update integrated BrowserRouter instead of the Router so I replaced
<Router history={createHistory}>
with
<BrowserRouter>
and everything is now working as it should
Read "Routes and Links are relative to their parent" on this React Router 6 article
https://medium.com/frontend-digest/whats-new-in-react-router-6-732b06cc83e4
by the way it's strange that you have <a> tags instead of Link components sometimes

React Router Link not rendering as clickable button

Not really sure I'm doing wrong here. I have a header component which I am then using in my main app component where my "page" components are conditionally rendered based on the app. Manually going to the routes works, however React Router's Link component is not rendering the links as clickable buttons thus I can't click on anything...
Here is my header component:
function Header(props) {
const links = props.links.map(link => {
return (
<Link to={{pathname: link.path}} key={link.title}>{link.title}</Link>
);
});
return(
<Navbar className="border-bottom" bg="transparent" expand="lg">
<Navbar.Brand href="#home">Garrett Love</Navbar.Brand>
<Navbar.Toggle className="border-0" aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="ml-auto">
{links}
</Nav>
</Navbar.Collapse>
</Navbar>
);
}
I did try setting the to prop as both to={link.path} and to="/about" but neither worked.
I'm just putting this component in my main app component like so:
render() {
return(
<Router>
<Container className="p-0" fluid={true}>
<Header links={this.state.headerLinks} />
<Route path="/" exact render={() => <Home title={this.state.home.title} subTitle={this.state.home.subTitle} />} />
<Route path="/about" render={() => <About title={this.state.about.title} />} />
<Footer />
</Container>
</Router>
);
}
I see you has header component and some app component
But in your app component you show <Gbar /> and dont show <Header />
The best way to fix this:
In Header
const Header = props =>
<Navbar className="border-bottom" bg="transparent" expand="lg">
<Navbar.Brand href="#home">Garrett Love</Navbar.Brand>
<Navbar.Toggle className="border-0" aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="ml-auto">
{props.links.map(link => <Link to={link.path} key={link.title}>{link.title}</Link>)}
</Nav>
</Navbar.Collapse>
</Navbar>
And in App:
render() {
return(
<Router>
<Container className="p-0" fluid={true}>
<Header links={this.state.headerLinks} />
<Route path="/" exact render={() => <Home title={this.state.home.title} subTitle={this.state.home.subTitle} />} />
<Route path="/about" render={() => <About title={this.state.about.title} />} />
<Footer />
</Container>
</Router>
);
}

Reactjs - props not changing during Routing

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>

Issue with React Router and Semantic UI React

I am new to ReactJS and have an issue with using "Menu.Item" (from Semantic UI React) and React Router.
My imports etc will be left out of the below code but they are all working fine.
My "App.jsx" constructor is as follows:
constructor(props) {
super(props);
this.state = {
activeItem: 'home',
loading: true,
messages: [],
};
}
The "App.jsx" render return is this:
<Router>
<Container className="main">
<Navbar
onItemClick={selected => this.setState({ activeItem: selected })}
isActive={this.state.activeItem}
/>
<Container className="main-content">
<Switch>
<Route exact path="/" component={Home} />
<Route path="/dashboard" component={Dashboard} />
<Route path="/user" component={User} />
</Switch>
</Container>
</Container>
</Router>
For clarification, the Home, User, and Dashboard components are just simple Div's that has their respective names in them.
My Navbar is as follows:
class Navbar extends Component {
onItemChange = (e, { name }) => this.props.onItemClick(name);
render() {
return (
<div>
<Container>
<Menu secondary stackable widths={4}>
<Menu.Item>
<img src={Logo} alt="header" />
</Menu.Item>
<Menu.Item
as={NavLink}
to="/"
name="home"
active={this.props.isActive === 'home'}
onClick={(event, name) => this.handleClick(name.name, name.to)}
>
<Icon name="home" size="large" />
<p>Home</p>
</Menu.Item>
<Menu.Item
as={NavLink}
to="/dashboard"
name="Data"
active={this.props.isActive === 'Data'}
onClick={this.onItemChange}
>
<Icon name="dashboard" size="large" />
<p>Dashboard</p>
</Menu.Item>
<Menu.Item
as={NavLink}
to="/user"
name="user"
active={this.props.isActive === 'user'}
onClick={this.onItemChange}
>
<Icon name="user" size="large" />
<p>User</p>
</Menu.Item>
</Menu>
</Container>
</div>
);
}
}
As you can see from the above Navbar, I am using NavLink within the Menu.Item.
Here is where the issue arises. When I start my application it works fine, 'home' is displayed, however when I click on a link in the Menu, the application crashes.
Any ideas on how to fix this? I also want to have it that the 'isActive' will update with the current route in the menu.
Any input would be greatly appreciated.
I fixed it for anyone looking into it by simplifying the code, here is the Navbar component:
const Navbar = () => (
<div>
<Container>
<Menu secondary stackable widths={4}>
<Menu.Item>
<img src={Logo} alt="header" />
</Menu.Item>
<Menu.Item as={NavLink} to="/" name="home">
<Icon name="home" size="large" />
<p>Home</p>
</Menu.Item>
<Menu.Item as={NavLink} to="/data" name="data">
<Icon name="dashboard" size="large" />
<p>Dashboard</p>
</Menu.Item>
<Menu.Item as={NavLink} to="/user" name="user">
<Icon name="user" size="large" />
<p>User</p>
</Menu.Item>
</Menu>
</Container>
</div>
);
Less does more, it seems. Hope this helps anyone.

Categories