I want to be able to have a Portfolio page (example.com/portfolio), and a dynamic route for individual case studies (example.com/portfolio/case-study/dynamic-url-slug). Currently, the new component that should render in its own page is still rendering within the page (understandable, as the markup declares the route within the containing div). But how do I get it to render on its own page?
App.js (where all routes are declared)
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from './components/Pages/Home/Home';
import About from './components/Pages/About/About';
import Portfolio from './components/Pages/Portfolio/Potfolio';
import CaseStudy from './components/Pages/Portfolio/CaseStudyPage';
export default class App extends Component {
render() {
return (
<div className="icon-container" id="outer-container">
<div className="pages">
<Switch>
<Route exact path='/' component={ Home } />
<Route path='/about' component={ About } />
<Route path='/portfolio' component={ Portfolio } />
<Route exact path={`/portfolio/case-study/:caseSlug`}
render={(props) => <CaseStudy />} />
</Switch>
</div>
</div>
)
}
}
Portfolio.js
import React, { Component } from 'react';
import '../styles/vendor/swiper/swiper.min.css';
import Swiper from 'react-id-swiper';
import { Link, Route } from 'react-router-dom';
import CaseStudyPage from './Pages/Work/CaseStudyPage';
const case_studiesURL = "http://myprivateblogapi.com/wp-json/wp/v2/case_studies?_embed";
const case_URL = '/portfolio/case-study/';
export default class Portfolio extends Component {
constructor(props) {
super(props);
this.state = {
case_studies: [],
isLoading: true,
requestFailed: false
}
}
componentDidMount() {
fetch(case_studiesURL)
{/* fetching all the appropriate data */}
}
renderPortfolioItem(data) {
return props => <CaseStudyPage data={data} {...props} />
}
render() {
if(this.state.isLoading) return <span>Loading...</span>
const params = {
{/* swiper parameters */}
}
let case_studies_items = this.state.case_studies.map((case_studies_item, index) => {
return (
<div className="portfolio-slide" id={`swiper-slide-${index}`}
key={index}
>
<Link className="portfolio-link"
to={`${case_URL}${case_studies_item.slug}`}>
<h3 className="portfolio-swiper--slide-title"> {case_studies_item.title.rendered}</h3>
</Link>
<Route exact path={`${case_URL}:caseSlug`}
render={this.renderPortfolioItem(case_studies_item)} />
</div>
)
});
return(
<div className="portfolio-swiper--container">
<Swiper {...params}>
{case_studies_items}
</Swiper>
</div>
)
}
}
You should define a route for each different views in react router,
<Switch>
<Route exact path='/' component={ Home } />
<Route exact path='/about' component={ About } />
<Route exact path='/portfolio' component={ Portfolio } />
<Route exact path='/portfolio/case-study' component={ CaseStudy } />
<Route exact path='/portfolio/case-study/:caseSlug' component {CaseStudyDetails} />
</Switch>
and you don't need to create a render method to pass props to your view components. You can easily reach router props inside of a react component if it is already rendered into Router,
this.props.match
this.props.location
this.props.history
as an example you can get your dynamic parameter inside of CaseStudy component like,
this.props.match.caseSlug
Related
I am trying to render the h1 tag when following the path '/', however, when I refresh the page the text doesn't show up. I have already switched the tags with tags as I was following an old tutorial. I am using react-router-dom v6.
HomePage.Js
import React, { Component } from "react";
import RoomJoinPage from "./RoomJoinPage";
import CreateRoomPage from "./CreateRoomPage";
import { BrowserRouter as Router, Routes, Route, Link, Redirect } from "react-router-dom";
export default class HomePage extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Router>
<Routes>
<Route path='/'>
<h1>This is the home page</h1>
</Route>
<Route path='/join' element={ <RoomJoinPage /> } />
<Route path='/create' element={ <CreateRoomPage/> } />
</Routes>
</Router>
);
}
}
App.Js
import React, { Component } from "react";
import { render } from "react-dom";
import HomePage from "./HomePage";
export default class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<HomePage />
</div>
);
}
}
const appDiv = document.getElementById("app");
render(<App />, appDiv);
set your routing in app.js like this
<Router>
<Switch>
<Route exact path='/'>
<h1>This is the home page</h1>
</Route>
<Route exact path='/join'> </Route>
<Route exact path='/create'> </Route>
</Switch>
</Router
I can get the data of the players when i Route to the Players Component,
but when i click on the Link Tags, the PLayersContainer Component is not opening.
This is my App.js File.
import React, { Component } from 'react'
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import Players from './Components/Players'
import PlayersContainer from './Components/Container/playersContainer'
import Navigation from './Components/Navigation';
export default class App extends Component {
state = {
players:[
{
id:1,
name:'Ronaldo'
},
{
id:2,
name:'Messi'
}
]
}
render() {
return (
<Router>
<Navigation />
<Switch>
<Route path="/players" render={(props) => <Players {...props} players={this.state.players} />} />
<Route exact path="/players/:id" render={PlayersContainer} />
</Switch>
</Router>
)
}
}
This is my Players Component.
import React from 'react'
import { Link } from 'react-router-dom'
export default function Players(props) {
const renderPlayers = () => {
let players = props.players.map(playerObj => <li> <Link to={`/players/${playerObj.id}`}> Player {playerObj.name} </Link></li>)
return players
}
return (
<div>
<ul>
{renderPlayers()}
</ul>
</div>
)
}
This is my PlayersContainer Component, where i want to render the individual data of the Player.
import React from 'react'
import { Link } from 'react-router-dom'
export default function PlayersContainer(props) {
const renderPlayers = () => {
console.log(props);
}
return (
<div>
<ul>
{renderPlayers()}
</ul>
</div>
)
}
You have the wrong Route marked as exact. The way its written currently, anything beginning with /players will match the first route. Since its in a switch, only the first match will be rendered.
Change it from:
<Route path="/players" render={(props) => <Players {...props} players={this.state.players} />} />
<Route exact path="/players/:id" render={PlayersContainer} />
to this:
<Route exact path="/players" render={(props) => <Players {...props} players={this.state.players} />} />
<Route path="/players/:id" render={PlayersContainer} />
Now only exactly /players will match the first route, and /players/id can continue past it to match the second.
I have several components to protect authentication. Then I made a new component called ProtectedRoute. In this ProtectedRoute function I only catch properties that are thrown, but somehow I only get the state from React-Context, and the props I send are unreadable, when in console.log() it is undefined.
ProtectedRoute.js:
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
import { withAuth } from './Context/AuthContext'
function ProtectedRoute(props) {
const {component: Component, ...rest} = props
console.log(Component)
return(
props.isLoggedIn ? <Route {...rest} component={Component} /> : <Redirect push to="/" />
)
}
export default withAuth(ProtectedRoute)
App.js:
render() {
return (
<BrowserRouter>
<AuthContextProvider>
<Switch>
<Route exact path="/" component={Login} />
<ProtectedRoute path="/portal" component={Main} />
</Switch>
</AuthContextProvider>
</BrowserRouter>
)
}
I have imported all required component btw, but if I change ProtectedRoute to normal <Route> by react-router, it can render component Main.
Is there something wrong with my code?
I am trying to learn how to redirect through pages using React.
I have tried to write some code on my own but i keep getting problems. I Created a route class for the class path, and 2 classes to move through. And the route class is imported to the app class. I am not pasting any data from the second class because its a written paragraph to be displayed.
This is what i have done:
import React from 'react'
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
import Firsttry from './firsttry'
import Comp2 from "./comp2";
const Routes = () => (
<Router>
<Switch>
<Route path="/" component={Firsttry} />
<Route path="/comp2" component={Comp2} />
</Switch>
</Router>
);
export default Routes;
Second class:
import React, { Component } from "react";
import { Redirect } from "react-router-dom";
class Firsttry extends Component {
constructor(props) {
super(props);
this.state = {
redirect: false
};
}
onclick = () => {
this.setState({
redirect: true
});
};
render() {
if (this.state.redirect) {
return <Redirect to="/comp2" />;
}
return (
<div>
<p> hello</p>
<button onClick={this.onclick}>click me</button>
</div>
);
}
}
export default Firsttry;
Switch the routes. May be always your first route is getting hit and Comp2 is never rendered.
<Switch>
<Route path='/comp2' component={Comp2} />
<Route path='/' component={Firsttry}/>
</Switch>
Or you have another option: adding exact prop to your route.
<Switch>
<Route exact path='/' component={Firsttry}/>
<Route exact path='/comp2' component={Comp2} />
</Switch>
Only one Route inside a Switch can be active at a time, and / will match every route. You can add the exact prop to the / route to make sure it will only match on the root path.
const Routes = () => (
<Router>
<Switch>
<Route exact path="/" component={Firsttry} />
<Route path="/comp2" component={Comp2} />
</Switch>
</Router>
);
I have a component (DatasetPage) which renders some images of different datasets for an image selected. This depends on the tab clicked on the navigation top bar. The thing is that one of the dataset in my case is very big and so it takes more time to load the page. If I wait until the page is loaded everything works well but, if I click into another tab (another dataset) before the reducer delivers the properties to my component (ImageWithRelateds), the new page is loaded with the information of the other(last) dataset, which was not loaded yet.
So, I have thought about a solution which could be block the navigation through the navigation bar while I have the Loading running. But the thing is that this loading thing is controlled in the ImageWithRelateds.js component and the navigation bar is controlled from App.js. So I would need to access from App.js to the isLoading attribute of ImageWithRelateds.js (which I already have) but I don't know how to do it. I just found ways to access from children to parent attributes but not backwards. If you could help me with that or just proposing another solution I would be very grateful.
App.js
import React, { Component } from 'react';
import { IndexLink } from 'react-router';
import '../styles/app.scss';
class App extends Component {
constructor(props){
super(props);
this.options = this.props.route.data;
}
renderContent(){
if(this.props.route.options) {
return(<div className="navbar-header nav">
<a className="navbar-brand" id="title" href="/" >
IMAGES TEST WEB
</a>
<li className="nav-item" key={`key-9999`}>
<IndexLink to='/home' className="nav-link active" href="#">HOME</IndexLink>
</li>
{this.props.route.options.map((opt,i)=>{
return this.returnOptions(opt,i);
})}
</div>
);
}
}
returnOptions(opt,i){
return(<li className="nav-item" key={`key-${i}`}>
<IndexLink to={opt.link} className="nav-link active"
href="#">{opt.name}</IndexLink>
</li>);
}
render() {
return (
<div className="main-app-page">
<nav className="navbar navbar-default color-navbar fixed">
<div className="container-fluid">
{this.renderContent()}
</div>
</nav>
<div className="content">
{this.props.children}
</div>
</div>
);
}
}
export default App;
Routes.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Route, IndexRoute, browserHistory, Link } from 'react-router';
import App from './common/App';
import NotFound from './common/NotFound';
import Store from './store';
import Home from './components/Home';
import DatasetPage from './components/Images/DatasetPage';
import ImageWithRelateds from './components/Images/ImageWithRelateds';
import { options_NavBar } from './customize.js';
import { getQimList, resetQimList} from './actions/index';
const Test = ()=>{
return(<h2 style={{"paddingLeft":"35%"}} >W E L C O M E !</h2>)
};
export default (
<Route path="/" components={App} options={options_NavBar} history={browserHistory}>
<IndexRoute components={Test}/>
<Route path="/home" component={Home} />
<Route path="images" >
<Route path="oxford" component={DatasetPage} onEnter={()=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('oxford'));
}} />
<Route path="paris" component={DatasetPage} onEnter={()=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('paris'));
}} />
<Route path="instre" component={DatasetPage} onEnter={(e)=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('instre'));
}} />
<Route path=":id" component={ImageWithRelateds} />
</Route>
<Route path="*" component={NotFound} />
</Route>
);
Thank you so much!
One of the most basic principle in react that the parent give props to children, and the children emit events to the father (to avoid 2 way binding)
so, your App.js should have state, with isLoading variable, and to the ImageWithRelateds component you should pass an event (function) something like this:
<Route path=":id" render={(props) => <ImageWithRelateds {...props} onFinishLoading={loadingFinished} />}>
and inside your component (that should be with state) should have function like this:
function loadingFinished() {
this.setState(prev => ({ ...prev, isLoading: false }))
}
and then, you would know inside you App.js if the loading inside the ImageWithRelateds component finished, and then you would able to do any validation you would like
I suggest to you to read this article about passing events (functions) to components, why it's needed and how to do it effectively
Hope that helped!
Edit:
your final Routes.js code should look something like that:
export default class Routes extends React.Component {
constructor() {
super();
this.state = { isLoading: false };
this.onLoadingFinishded = this.onLoadingFinishded.bind(this);
}
onLoadingFinishded() {
this.setState(state => {
...state,
isLoading: false
});
}
render() {
return <Route path="/" components={App} options={options_NavBar} history={browserHistory}>
<IndexRoute components={Test}/>
<Route path="/home" component={Home} />
<Route path="images" >
<Route path="oxford" component={DatasetPage} onEnter={()=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('oxford'));
}} />
<Route path="paris" component={DatasetPage} onEnter={()=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('paris'));
}} />
<Route path="instre" component={DatasetPage} onEnter={(e)=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('instre'));
}} />
<Route path=":id" render={(props) => <ImageWithRelateds
{...props}
onLoadingFinished={this.onLoadingFinishded} />} />
</Route>
<Route path="*" component={NotFound} />
</Route>
}
}
(i can't ensure that code exactly running because i don't have all of your project, but that most likely it)