My question might seem stupid because I don't have enough background in React JS.
I have this component:
import { BrowserRouter, Route, NavLink } from "react-router-dom";
import CourseInfo from "./CourseInfo";
import OneCourse from "./OneCourse";
class CourseList extends Component {
render() {
return (
<div>
<BrowserRouter>
<div className="row courses">
{this.props.corses.map(course => (
<NavLink key={course._id} to={`/courses/profile/${course._id}`}>
<OneCourse course={course} />
</NavLink>
))}
</div>
<Route
exact
path={`/courses/profile/:id`}
render={({ match }) => (
<CourseInfo
index={match.params.id}
course={
this.props.corses.filter(el => el._id === match.params.id)[0]
}
/>
)}
/>
</BrowserRouter>
</div>
);
}
}
When I click on OneCourse component, it shows me the CourseList component in the same page with the component CourseInfo added in the bottom.
How can I send user to a new page containing only CourseInfo, knowing that I have parameters to send from this component to CourseInfo?
I want to show CourseInfo component in a different page that doesn't contain the CourseList
<NavLink> is just fine. For the rest, you might use the <Switch> component:
import { BrowserRouter, Route, NavLink, Switch } from 'react-router-dom';
class CourseList extends Component {
render() {
return (
<div>
<BrowserRouter>
<Switch>
<Route
exact
path={`/`} render={({ match }) => (
<div className="row courses">
{this.props.corses.map(course => (
<NavLink key={course._id} to={`/courses/profile/${course._id}`}>
<OneCourse course={course} />
</NavLink>
))}
</div>
)}
/>
<Route
exact
path={`/courses/profile/:id`}
render={({ match }) => (
<CourseInfo
index={match.params.id}
course={
this.props.corses.filter(el => el._id === match.params.id)[0]
}
/>
)}
/>
</Switch>
</BrowserRouter>
</div>
);
}
}
Quoting from the docs:
Switch is unique in that it renders a route exclusively. In contrast, every that matches the location renders inclusively.
With complex routing the render props approach gets confusing. Moving the routing to a separate component is a better approach:
import React from 'react';
import { BrowserRouter, Route, Link, Switch } from "react-router-dom";
import CourseList from './CourseList';
import Course from './Course';
function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={CourseList} />
<Route exact path="/courses/profile/:id" component={Course} />
</Switch>
</BrowserRouter>
);
}
export default App;
Then your CourseInfo component looks like:
class CourseList extends Component {
render() {
return (
<div>
<div className="row courses">
{this.props.corses.map(course => (
<NavLink key={course._id} to={`/courses/profile/${course._id}`}>
<OneCourse course={course} />
</NavLink>
))}
</div>
</div>
);
} }
The official documentation provides plenty examples.
Related
I'm trying to have a Home Page and with differents links redirect to the differents pages of my app.
I used <Routes> and <Route> to redirect them but is not working. It stay in blank.
I want to Navebar be the layout here, so I read that it must contain the other Routes inside of it
import React from 'react';
import { Route, Routes } from 'react-router';
import './App.scss';
import Navbar from './components/Navbar/Navbar'
import Home from './components/Home/index'
import Contact from './components/Contact'
const App = () => {
return (
<>
<Routes>
<Route path='/' element={<Navbar/>}>
<Route exact={true} index element={<Home/>}></Route>
<Route exact={true} path='contact' element={<Contact/>}></Route>
</Route>
</Routes>
</>
);
}
export default App;
index.js
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
This is the Navbar (which is the onlyone is showing)
class Navbar extends Component{
state = { clicked: false}
handleClick = () =>{
this.setState({clicked: !this.state.clicked})
}
render(){
return(
<>
<nav className='navbar-items'>
<img alt='mRadio' className='navbar-logo' href="../Home/index" src={require('../../assets/images/mRadio.png')}></img>
<div className='menu-icon' onClick={this.handleClick}>
<i className={this.state.clicked ? 'fas fa-times' : 'fas fa-bars'}></i>
</div>
<ul className={this.state.clicked ? 'nav-menu active' : 'nav-menu'}>
{MenuItems.map((item, index)=>{
return (
<li key={index}>
<a className={item.cName} href={item.url}>{item.title}</a>
</li>
)
})
}
</ul>
</nav>
</>
)
}
}
And this is the Home page:
const Index = () => {
return (
<div className='main'>
<video src={videomRadio} autoPlay loop muted/>
</div>
);
}
And this is a Third page:
const Index = () => {
return (
<div>
<p>CONTACT PAGE</p>
</div>
);
}
If you re using the latest version 6, don't use Switch, now is become Routes, try to change the path like this for example if is nested component:
import { Route, Routes } from 'react-router-dom'; //just in case
//...
const App = () => {
return (
<>
<Routes>
<Route path='/' element={<Navbar/>}>
<Route index element={<Home/>} />
<Route path='contact' element={<Contact/>}/>
</Route>
</Routes>
</>
);
}
export default App;
create a child in home and third page.that gives access to that page directly.
I am Trying to reach the <Gallery/> Component using a Menu button with React-Router Link
so the code is for the Menu
Menu.jsx
import { Link } from 'react-router-dom';
export default function Menu({ menuOpen, setMenuOpen }) {
return (
<div className={"menu " + (menuOpen && "active")}>
<ul>
<li onClick={() => setMenuOpen(false)}>
<Link to="/">Home Page</Link>
</li>
<li onClick={() => setMenuOpen(false)}>
<Link to="/Gallery">Gallery</Link>
</li>
</ul>
</div>
);
}
and the code for APP.jsx:
import './App.scss';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { useState } from 'react';
import Gallery from './components/Gallery/Gallery';
import Menu from './components/menu/Menu';
import Topbar from './components/topbar/Topbar';
import FooterComp from './components/Footer/FooterComp';
const App = () => {
const [menuOpen, setMenuOpen] = useState(false);
return (
<>
<Router>
<Topbar menuOpen={menuOpen} setMenuOpen={setMenuOpen} />
<Menu menuOpen={menuOpen} setMenuOpen={setMenuOpen} />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/Gallery" elemtent={<Gallery />} />
</Routes>
<FooterComp />
</Router>
</>
)
}
export default App
When I click the button which is supposed to route to the <Gallery/> Component it routes to an empty component and this warning is displayed in the console
Matched leaf route at location "/Gallery" does not have an element. This means it will render an with a null value by default resulting in an "empty" page.
I searched for this problem and only router-dom version related fixes are there and you can see I'm using the correct v6 props and components.
You have a typo. element
Change
<Route path="/Gallery" elemtent={<Gallery />} />
to
<Route path="/Gallery" element={<Gallery />} />
I've tried all possible versions but goBack() button does not work.
Not sure what I am doing wrong but I was following this solution:
react-router (v4) how to go back?
Anyway here is the code I am trying and I have feeling that there is something to do with HashRouter.
Also How can I put the button in Navbar instead of calling it in App?
import React from 'react';
import { HashRouter, Route, withRouter } from 'react-router-dom';
import Home from "./components/Home";
import Navbar from "./components/Navbar";
import store from './store'
import PrivateRoute from './components/auth/PrivateRoute'
class App extends React.Component {
constructor(props){
super(props);
this.goBack = this.goBack.bind(this); // i think you are missing this
}
componentDidMount(){
store.dispatch(loadUser())
}
goBack(){
this.props.history.goBack();
}
render(){
return (
<Provider store={store}>
<HashRouter basename="/">
<Navbar />
<button onClick={this.goBack()}>Go Back</button>
<Route exact path="/" component={Home}/>
<PrivateRoute path="/aeons" component={AeonsList} />
</HashRouter>
</Provider>
I had an example. Please check it, hope it helps you.
import React from "react";
import {BrowserRouter as Router, Switch, Route, Link} from "react-router-dom";
import {withRouter} from "react-router";
export default function BasicExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/about/insideabout">Inside About</Link>
</li>
</ul>
<hr/>
<Switch>
<Route exact path="/">
<Home/>
</Route>
<Route exact path="/about">
<About/>
</Route>
<Route path="/about/insideabout">
<InsideAbout/>
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
const About = withRouter(({history, ...props}) => (
<div>
<h1>
About
<hr/>
<button onClick={() => {
// history.push('/')
history.goBack(-1);
}}>go back
</button>
</h1>
</div>
));
const InsideAbout = withRouter(({history, ...props}) => (
<h1 {...props}>
Inside About
<hr/>
<button onClick={() => {
history.goBack();
}}>go back
</button>
<button onClick={() => {
history.go(-2);
}}>go home
</button>
</h1>
));
This how I solved it: Create another component with Go Back button and call it anywhere you want
import React from 'react';
import { withRouter } from 'react-router';
const GoBackButton = withRouter(({history, ...props}) => (
<div>
<button onClick={() => {
// history.push('/')
history.goBack(-1);
}}>go back
</button>
</div>
));
export default GoBackButton
In my react project, this is my App.js:
import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Modal from "./Modal";
export default function BasicExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
</ul>
<hr />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/modal">
<Modal />
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
<p>
Please <Link to="/modal/1">Click Here</Link> for see details.
</p>
</div>
);
}
When you click on "Click Here", the modal was open, but my home page will be disappear. how can open this modal without destroying the home page ?
DEMO HERE:
https://codesandbox.io/s/react-router-basic-2g9t1
Modals should not be in a route as they are supposed to be on top of another page, not a page themshelves. If you want my opinion I would suggest you to put the modal in any of the pages and control if it is opened or not with a react state:
import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Modal from "./Modal";
export default function BasicExample() {
return (
<Router>
<div>
<Switch>
<Route exact path="/" component={Home} />
</Switch>
</div>
</Router>
);
}
const Home = () => {
const [ isModalOpened, setModalOpened ] = useState(false);
return (
<div>
<h2>Home</h2>
<button onClick={() => setModalOpened(!isModalOpened)}
<Modal isOpened={isModalOpened}>
...modal content here
</Modal>
</div>
);
}
And your modal component should look like something like this:
const Modal = ({ isOpened, children }) => (
<div>
{
isOpened &&
{ children }
}
</div>
)
If this helps you make sure to mark it as a good response!
I want to show/hide the navigation bar depending upon logged in status of the user. Once the user is authenticated, I store the access-token in local storage. I have tried to show/hide the navigation bar by checking if access-token exists or not. But it needs a hard refresh.
Please find the header component: /components/app-header.js
const AppHeader = () => (
<Navbar color="light" light expand="md">
<NavbarBrand href="/">TestBrand</NavbarBrand>
<Nav navbar>
<NavItem>
<Link className="lnk" to='/users'>Users</Link>
</NavItem>
</Nav>
<Nav className="ml-auto" navbar>
<NavItem>
<Link className="lnk" to='/logout'>Logout</Link>
</NavItem>
</Nav>
</Navbar>
)
The file which handles all the routes is as below (routes/index.js):
import React from 'react';
import { BrowserRouter, Route, Link, Switch } from 'react-router-dom';
import { AppHeader } from '../components';
export default () => (
<BrowserRouter>
<div>
{
localStorage.getItem('access-token') &&
<div>
<AppHeader />
</div>
}
<Switch>
<Route exact path="/users" component={Users} />
<Route exact path="/users/add" component={Users} />
<Route exact path="/users/:id" component={Users} />
</Switch>
</div>
</BrowserRouter>
)
The main App just contains the following code:
import React, { Component } from 'react';
import Routes from '../routes';
import '../style.css';
class App extends Component {
render() {
return (
<div>
<Routes />
</div>
)
}
}
export default App;
I do not want to refresh the page, as it defeats the very purpose of SPA. How can I achieve that?
Make Routes a stateful Component.
import React from 'react';
import { BrowserRouter, Route, Link, Switch } from 'react-router-dom';
import { AppHeader } from '../components';
class Routes {
this.state = {loggedIn: false}
componentDidMount() {
if(localStorage.getItem('access-token')) {
this.setState({loggedIn: true})
}
//attach an event listener to the window object for storage event.
$(window).on('storage',() => {
if(localStorage.getItem('access-token')) {
this.setState({loggedIn: true})
}
});
}
render() {
return (
<BrowserRouter>
<div>
{
this.state.loggedIn &&
<div>
<AppHeader />
</div>
}
<Switch>
<Route exact path="/users" component={Users} />
<Route exact path="/users/add" component={Users} />
<Route exact path="/users/:id" component={Users} />
</Switch>
</div>
</BrowserRouter>)
}
}
export default Routes;
After the component is mounted you are listening to localStorage updates. if local storage is changed it will update the state of the component as required.